Introduction

Quand on veut appliquer une meme operation un grand nombre de fois, on cree souvent une fonction qui realise cette operation et on l’applique de facon repetee avec une boucle. Le package dplyr et sa syntaxe sans guillemets peut se retrouver en difficulte dans cette situation, et plusieurs evolutions du package ont tente d’y remedier :

Dans l’article https://www.tidyverse.org/articles/2019/06/rlang-0-4-0/, Rstudio fait amende honorable et choisit de revenir a plus de simplicite et d’efficacite avec le nouvel operateur “{{ }}” (prononcer curly-curly). Et l’article plus recent https://www.tidyverse.org/blog/2020/02/glue-strings-and-tidy-eval/ complete le paysage avec l’operateur {}.

Pour resumer ce qu’on va voir dans les exemples qui suivent :

Fonctions “a la dplyr” avec {{}}

Le code dplyr classique ne fonctionne pas bien a l’interieur d’une fonction, mais en entourant tous les parametres avec 2 accolades on regle la plupart des difficultes :

library("dplyr")

max_by <- function(data, var, by) {
  data %>% group_by({{ by }}) %>% summarise(maximum = max({{ var }}, na.rm = TRUE))
}

starwars %>% max_by(height)

starwars %>% max_by(height, by = gender)
# A tibble: 1 x 1
  maximum
    <int>
1     264
# A tibble: 3 x 2
  gender    maximum
  <chr>       <int>
1 feminine      213
2 masculine     264
3 <NA>          183

On peut passer des formules de calculs si on veut :

summarise_by <- function(data, ..., by) {
  data %>% group_by({{ by }}) %>% summarise(...)
}

starwars %>% summarise_by(average = mean(height, na.rm = TRUE),
                          maximum = max(height, na.rm = TRUE),
                          by = gender)
# A tibble: 3 x 3
  gender    average maximum
  <chr>       <dbl>   <int>
1 feminine     165.     213
2 masculine    177.     264
3 <NA>         181.     183

Et on peut utiliser les {} simples suivis de := pour nommer le champ calcule avec une chaine de caracteres :

max_by_nom <- function(data, var, by, nom = "mon_champ") {
  data %>% group_by({{ by }}) %>% summarise("{nom}" := max({{ var }}, na.rm = TRUE))
}

starwars %>% max_by_nom(height, by = gender)

starwars %>% max_by_nom(height, by = gender, nom = "ma_variable")
# A tibble: 3 x 2
  gender    mon_champ
  <chr>         <int>
1 feminine        213
2 masculine       264
3 <NA>            183
# A tibble: 3 x 2
  gender    ma_variable
  <chr>           <int>
1 feminine          213
2 masculine         264
3 <NA>              183

Exemples de fonctions et boucles avec les noms entre guillemets

On utilise ici la notation classique de R base .data[[ma_variable]] dans les fonctions de dplyr.

max_by_char <- function(data, var_char, by_char) {
data %>% group_by(.data[[by_char]]) %>% summarise(maximum = max(.data[[var_char]], na.rm = TRUE))
}

starwars %>% max_by_char("height", by = "gender")
# A tibble: 3 x 2
  gender    maximum
  <chr>       <int>
1 feminine      213
2 masculine     264
3 <NA>          183

Et pour finir on realise une boucle sur 2 champs.

# fonction qui calcule un ratio
ratio_char <- function(data, num_char, denom_char, ratio_char) {
  data %>% mutate("{ratio_char}" := .data[[num_char]] / .data[[denom_char]])
}

vect_numerateur = c("Sepal.Width", "Sepal.Length")
vect_denom = c("Petal.Width", "Petal.Length")
vect_ratio = c("ratio_W", "ratio_L")

dtf = iris %>% filter(Sepal.Length > 7.2) %>% select(-Species)

for (i in 1:length(vect_ratio)) {
  dtf = dtf %>%  ratio_char(vect_numerateur[i], vect_denom[i], vect_ratio[i])
}

dtf
  Sepal.Length Sepal.Width Petal.Length Petal.Width  ratio_W  ratio_L
1          7.6         3.0          6.6         2.1 1.428571 1.151515
2          7.3         2.9          6.3         1.8 1.611111 1.158730
3          7.7         3.8          6.7         2.2 1.727273 1.149254
4          7.7         2.6          6.9         2.3 1.130435 1.115942
5          7.7         2.8          6.7         2.0 1.400000 1.149254
6          7.4         2.8          6.1         1.9 1.473684 1.213115
7          7.9         3.8          6.4         2.0 1.900000 1.234375
8          7.7         3.0          6.1         2.3 1.304348 1.262295

retour au debut du document