web-dev-qa-db-fra.com

évaluation standard dans dplyr: résumer une variable donnée sous forme de chaîne de caractères

Je veux faire référence à un nom de colonne inconnu dans un summarise. Les fonctions d'évaluation standard introduites dans dplyr 0.3 Permettent de référencer les noms de colonnes à l'aide de variables, mais cela ne semble pas fonctionner lorsque vous appelez une fonction base R dans par exemple un summarise.

library(dplyr)

key <- "v3"
val <- "v2"
drp <- "v1"

df <- data_frame(v1 = 1:5, v2 = 6:10, v3 = c(rep("A", 3), rep("B", 2)))

Le df ressemble à ceci:

> df
Source: local data frame [5 x 3]

  v1 v2 v3
1  1  6  A
2  2  7  A
3  3  8  A
4  4  9  B
5  5 10  B

Je veux supprimer v1, grouper par v3 et additionner v2 pour chaque groupe:

df %>% select(-matches(drp)) %>% group_by_(key) %>% summarise_(sum(val, na.rm = TRUE))

Error in sum(val, na.rm = TRUE) : invalid 'type' (character) of argument

La version NSE de select() fonctionne très bien, car elle peut correspondre à une chaîne de caractères. La version SE de group_by() fonctionne très bien, car elle peut désormais accepter des variables comme arguments et les évaluer. Cependant, je n'ai pas trouvé de moyen d'obtenir des résultats similaires lors de l'utilisation des fonctions de base R dans les fonctions dplyr.

Choses qui ne fonctionnent pas:

df %>% group_by_(key) %>% summarise_(sum(get(val), na.rm = TRUE))
Error in get(val) : object 'v2' not found

df %>% group_by_(key) %>% summarise_(sum(eval(as.symbol(val)), na.rm = TRUE))
Error in eval(expr, envir, enclos) : object 'v2' not found

J'ai vérifié plusieursliésquestions , mais aucune des solutions proposées n'a fonctionné pour moi jusqu'à présent.

46
Ajar

Avec la sortie du paquet rlang et la mise à jour 0.7.0 de dplyr, c'est maintenant assez simple.

Lorsque vous souhaitez utiliser une chaîne de caractères (par exemple, "v1") comme nom de variable, vous n'avez qu'à:

  1. Convertissez la chaîne en un symbole à l'aide de sym() à partir du package rlang
  2. Dans votre appel de fonction, écrivez !! Devant le symbole

Par exemple, vous feriez ce qui suit:

my_var <- "Sepal.Length"
my_sym <- sym(my_var)
summarize(iris, Mean = mean(!!my_sym))

De manière plus compacte, vous pouvez combiner l'étape de conversion de votre chaîne en symbole avec sym() et en la préfixant avec !! Lors de l'écriture de votre appel de fonction.

Par exemple, vous pourriez écrire:

my_var <- "Sepal.Length"
summarize(iris, mean(!!sym(my_var)))


Pour revenir à votre exemple d'origine, vous pouvez procéder comme suit:

library(rlang)

key <- "v3"
val <- "v2"
drp <- "v1"

df <- data_frame(v1 = 1:5, v2 = 6:10, v3 = c(rep("A", 3), rep("B", 2)))

df %>% 
  # NOTE: we don't have to do anything to `drp`
  # since the matches() function expects a character string
  select(-matches(drp)) %>% 
  group_by(!!sym(key)) %>% 
  summarise(sum(!!sym(val), na.rm = TRUE))


Syntaxe alternative

Avec la version de rlang version 0.4.0, vous pouvez utiliser la syntaxe suivante:

my_var <- "Sepal.Length"
my_sym <- sym(my_var)
summarize(iris, Mean = mean({{ my_sym }}))

Au lieu d'écrire !!my_sym, Vous pouvez écrire {{ my_sym }}. Cela a l'avantage d'être sans doute plus clair, mais a l'inconvénient que vous devez convertir la chaîne en symbole avant de la placer à l'intérieur des crochets. Par exemple, vous pouvez écrire !!sym(my_var) mais vous ne peut pas écrire {{sym(my_var)}}

Détails supplémentaires

De toute la documentation officielle expliquant le fonctionnement de sym() et !!, Celles-ci semblent être les plus accessibles:

  1. vignette dplyr: Programmation avec dplyr

  2. La section du livre de Hadley Wickham "Advanced R" sur la métaprogrammation

26
bschneidr

Veuillez noter que cette réponse ne s'applique pas à dplyr >= 0.7.0, Mais aux versions précédentes.

[dplyr 0.7.0] a une nouvelle approche de l'évaluation non standard (NSE) appelée tidyeval. Il est décrit en détail dans vignette("programming").


La vignette dplyr sur l'évaluation non standard est utile ici. Vérifiez la section "Mélange de constantes et de variables" et vous constaterez que la fonction interp du package lazyeval pourrait être utilisée, et "[u] se as.name Si vous avez un caractère chaîne qui donne un nom de variable ":

library(lazyeval)
df %>%
  select(-matches(drp)) %>%
  group_by_(key) %>%
  summarise_(sum_val = interp(~sum(var, na.rm = TRUE), var = as.name(val)))
#   v3 sum_val
# 1  A      21
# 2  B      19
53
Henrik

Passe le .dots argument une liste de chaînes construisant les chaînes en utilisant paste, sprintf ou en utilisant l'interpolation de chaînes du package gsubfn via fn$list à la place de list comme nous le faisons ici:

library(gsubfn)
df %>% 
   group_by_(key) %>% 
   summarise_(.dots = fn$list(mean = "mean($val)", sd = "sd($val)"))

donnant:

Source: local data frame [2 x 3]

  v3 mean        sd
1  A  7.0 1.0000000
2  B  9.5 0.7071068
9
G. Grothendieck

Nouvelle mise à jour de dplyr:

La nouvelle fonctionnalité de dplyr peut vous y aider. Au lieu de chaînes pour les variables qui nécessitent une évaluation non standard, nous utilisons des quosures quo(). Nous annulons la citation avec une autre fonction !!. Pour en savoir plus voir cette vignette . Vous aurez besoin de la version développeur de dplyr jusqu'à la version complète.

library(dplyr) #0.5.0.9004+
key <- quo(v3)
val <- quo(v2)
drp <- "v1"

df <- data_frame(v1 = 1:5, v2 = 6:10, v3 = c(rep("A", 3), rep("B", 2)))
df %>% select(-matches("v1")) %>% 
  group_by(!!key) %>% 
  summarise(sum(!!val, na.rm = TRUE))
# # A tibble: 2 × 2
#      v3 `sum(v2, na.rm = TRUE)`
#   <chr>                   <int>
# 1     A                      21
# 2     B                      19
9
Pierre Lafortune