juba / questionr

R package to make surveys processing easier
https://juba.github.io/questionr/
81 stars 17 forks source link

Syntax forcats des add-in #125

Closed larmarange closed 3 years ago

larmarange commented 3 years ago

Un petit commentaire sur la syntaxe forcats produite par les add-in de questionr.

Voici le code généré pour un recodage :

hdv2003$nivetud_rec <- fct_recode(hdv2003$nivetud,
  "Primaire" = "N'a jamais fait d'etudes",
  "Primaire" = "A arrete ses etudes, avant la derniere annee d'etudes primaires",
  "Primaire" = "Derniere annee d'etudes primaires",
  "Secondaire" = "1er cycle",
  "Secondaire" = "2eme cycle",
  "Technique" = "Enseignement technique ou professionnel court",
  "Technique" = "Enseignement technique ou professionnel long",
  "Supérieur" = "Enseignement superieur y compris technique superieur"
)
hdv2003$nivetud_rec <- fct_explicit_na(hdv2003$nivetud_rec, "Manquant")

Une syntaxe plus en phase avec la logique du tidyverse serait :

hdv2003$nivetud_rec <- hdv2003$nivetud %>% 
  fct_recode(
    "Primaire" = "N'a jamais fait d'etudes",
    "Primaire" = "A arrete ses etudes, avant la derniere annee d'etudes primaires",
    "Primaire" = "Derniere annee d'etudes primaires",
    "Secondaire" = "1er cycle",
    "Secondaire" = "2eme cycle",
    "Technique" = "Enseignement technique ou professionnel court",
    "Technique" = "Enseignement technique ou professionnel long",
    "Supérieur" = "Enseignement superieur y compris technique superieur"
  ) %>%
  fct_explicit_na("Manquant")

A noter, l'opérateur %>% est bien exporté par forcats.

Pour le add-in Level orderind, il y a tout d'abord une petite erreur sur le premier écran Variable et Paramètres dans le paramètre Style de recodage. Il est écrit fct_recode (forcats) alors que cela devrait être fct_relevel (forcats).

De même pour la syntaxe produite :

hdv2003$sexe <- fct_relevel(
  hdv2003$sexe,
  "Femme", "Homme"
)

alors qu'une syntaxe plus proche de la philisophie du tidyverse serait :

hdv2003$sexe <- hdv2003$sexe %>%
  fct_relevel(
    "Femme", "Homme"
  )
juba commented 3 years ago

D'abord merci pour cette remarque et pour avoir repéré l'erreur sur fct_relevel, c'est corrigé.

Pour la syntaxe type tidyverse ou non, j'aurais deux (petites) réserves :

Du coup la solution idéale si on veut vraiment implémenter ça serait, il me semble, une case à cocher optionnelle. Mais je pense que ça va compliquer le code de manière un peu démesurée si il faut gérer toutes les possibilités de croisement fonction utilisée x pipe ou non...

larmarange commented 3 years ago

J'ai l'impression que depuis quelques années, l'utilisation du pipe à commencer à se généraliser de plus en plus en dehors de dplyr dans les différents packages qui gravitent autour du tidyverse. Il y a de plus en plus de packages qui l'importe et le réexporte. D'ailleurs, même forcats le réexporte. Le pipe est donc disponible même si on a juste chargé que ce package.

On peut voir sur stack ou sur d'autres sites de support des syntaxes comme d$var %>% mean(na.rm = TRUE). Si on regarde le récent projet gt de grammaire des tableaux, sa syntaxe repose sur le pipe et non sur un approche additive comme ggplot2.

On voit donc de plus en plus une syntaxe du type objet %>% tranform(option) et à ce titre ca ne me choque pas de voir hdv2003$sexe %>% fct_relevel("oui") en dehors d'un mutate mais je suis probablement influencé par mes lectures.

En termes d'enseignement, je comprends ton point de vue. Ceci dit, et c'est tout l'intérêt des add-in, ces derniers proposent déjà une syntaxe classique qui ne repose pas du tout sur forcats.

Pour ma part, lorsque je commence à présenter le tidyverse, j'introduis très tôt le pipe car c'est un élément essentiel pour permettre aux étudiants d'aller par eux-même chercher des exemples ou de la documentation en ligne. Ils tomberont presque à coup sûr sur un pipe.

larmarange commented 3 years ago

Note : plutot qu'une case à cocher, il y a aussi la possibilité, dans le choix du style de recodage, de proposer fct_recode avec ou sans pipe (pour ne pas alourdir l'interface) si on souhaite proposer les deux.

Sur le long terme (notamment pour une relecture du code sur un projet complexe), la rédaction avec pipe me semble (c'est un point de vue peut être subjectif) plus facile à relire, surtout si le code est sur plusieurs lignes (indentation plus logique).

juba commented 3 years ago

Oui, c'est vrai, une case à cocher ne serait pas l'idéal, ta solution est bien meilleure.

Je reste assez peu convaincu de l'intérêt du pipe quand on applique une seule opération à un vecteur, mais c'est vraiment une question de goût personnel. J'introduis aussi le pipe dans mes formations d'introduction à R, parce que c'est clairement un vrai plus pour la lisibilité et la compréhension du code, mais dans mon programme ça arrive après la présentation des addins de recodage (et je pense que je ne suis pas le seul).

Bref, c'est une bonne idée en tous cas, et je garde l'issue ouverte pour implémenter ça à l'occasion.

larmarange commented 3 years ago

La question peut en effet se poser pour une seule ligne. Après, cela évite de mélanger des éléments de natures différentes entre les parenthèses : objet <- function(objet, option1, option2) vs. objet <- objet %>% fonction(option1, option2).

Sur le code actuel qui est multiligne :

objet <- fonction(object,
  option1,
  option2
)

ce qui me semble une mauvaise pratique est de faire le retour à la ligne après le premier argument alors qu'une bonne pratique serait de le faire à l'ouverture de la parenthèse (même s'il n'y a pas de consensus là-dessus. dans tous les cas, c'est toujours difficile de faire adopter de bonnes pratiques d'indentation par les étudiants) :

objet <- fonction(
  objet,
  option1,
  option2
)

Mais dans ce cas là, l'objet de départ se trouve transférer sur la seconde ligne, après avoir indiquer la transformation.

Avec le pipe on n'a plus ce problème et on garde la bonne habitude de faire des retours à la ligne après l'ouverture des parenthèses (la règle la plus simple) :

objet <- objet %>%
  fonction(
    option1,
    option2
  )

Après je suis conscient qu'il n'y a pas une seule manière de faire dans R et qu'il s'agit de trouver un équilibre entre avantages et inconvénients.

juba commented 3 years ago

Oui, il n'y a clairement pas de bonne réponse à cette question, juste des préférences personnelles et quelques critères de choix plus ou moins objectifs :-)

Je n'avais pas pensé au fait que ça permet de différencier l'objet principal sur lequel on applique une fonction de ses arguments "secondaires". Je continue à penser que ça reste un peu overkill quand on applique qu'une seule fonction à l'objet, mais c'est vrai que ça peut être intéressant. et j'avoue qu'il y a des cas de figure où ça peut être plus lisible, genre :

x %>% mean(na.rm = TRUE) %>% round(2)

Un autre usage très utile aussi, mais plutôt en ligne de commande, c'est pour mettre un head ou un View derrière un data frame sans avoir à ajouter des parenthèses.

larmarange commented 3 years ago

Un autre usage très utile aussi, mais plutôt en ligne de commande, c'est pour mettre un head ou un View derrière un data frame sans avoir à ajouter des parenthèses.

Oh que oui !!!! ;-)

juba commented 3 years ago

L'Issue a presqu'un an, mais mieux vaut tard que jamais, j'ai essayé d'implémenter ta suggestion d'amélioration de la syntaxe tidyverse dans irec() et iorder(). Je ferme l'issue du coup, mais n'hésite pas à la rouvrir si tu vois quoi que ce soit qui pose problème.

Merci pour la suggestion et désolé pour le délai !