evaluación estándar en dplyr: summarise_ en variable dada como una cadena de caracteres

Quiero referirme a un nombre de columna desconocido dentro de un summarise . Las funciones de evaluación estándar introducidas en dplyr 0.3 permiten hacer referencia a los nombres de columna utilizando variables, pero esto no parece funcionar cuando se llama a una función R base , por ejemplo, en 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))) 

El df se ve así:

 > 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 

Quiero soltar v1, agrupar por v3 y sumr v2 para cada grupo:

 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 versión de NSE de select() funciona bien, ya que puede coincidir con una cadena de caracteres. La versión SE de group_by() funciona bien, ya que ahora puede aceptar variables como argumentos y evaluarlas. Sin embargo, no he encontrado una manera de lograr resultados similares cuando uso las funciones base R dentro de dplyr funciones dplyr .

Cosas que no funcionan:

 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 

Revisé varias preguntas relacionadas , pero ninguna de las soluciones propuestas me ha funcionado hasta ahora.

Con el lanzamiento del paquete rlang y la actualización 0.7.0 a dplyr, ahora es bastante simple.

Cuando desee utilizar una cadena de caracteres (por ejemplo, “v1”) como nombre de variable, simplemente:

  1. Convierta la cadena a un símbolo usando sym() del paquete rlang
  2. En tu llamada de función, escribe !! frente al símbolo

Por ejemplo, harías lo siguiente:

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

De forma más compacta, podría combinar el paso de convertir su cadena a un símbolo con sym() y prefijarlo con !! al escribir su llamada de función.

Por ejemplo, podrías escribir:

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

Para volver al ejemplo original, puede hacer lo siguiente:

 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)) 

Detalles adicionales

De toda la documentación oficial que explica cómo el uso de sym() y !! funciona, estos parecen ser los más accesibles:

  1. dplyr vignette: Progtwigción con dplyr

  2. La sección del libro de Hadley Wickham 'Advanced R' en metaprogtwigción

Tenga en cuenta que esta respuesta no se aplica a dplyr >= 0.7.0 , sino a versiones anteriores.

[ dplyr 0.7.0 ] tiene un nuevo enfoque para la evaluación no estándar (NSE) llamado tidyeval. Se describe en detalle en vignette("programming") .


La viñeta de dplyr sobre dplyr no estándar es útil aquí. Compruebe la sección “Mezcla de constantes y variables” y encontrará que la función interp del paquete lazyeval podría usarse, y “[u] se as.name si tiene una cadena de caracteres que le da un nombre 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 

Pase el argumento .dots una lista de cadenas que construyen las cadenas usando paste , sprintf o usando la interpolación de cadenas desde el paquete gsubfn a través de fn$list en lugar de la list como lo hacemos aquí:

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

dando:

 Source: local data frame [2 x 3] v3 mean sd 1 A 7.0 1.0000000 2 B 9.5 0.7071068 

Nueva actualización dplyr:

La nueva funcionalidad de dplyr puede ayudar con esto. En lugar de cadenas para las variables que necesitan una evaluación no estándar, usamos quosures quo() . Deshacemos las citas con otra función !! . Para más información sobre estos, vea esta viñeta . Necesitará la versión del desarrollador de dplyr hasta la versión completa.

 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)` #   # 1 A 21 # 2 B 19