r dplyr nse tidyeval

¿Por qué es enquo+!! preferible sustituir+eval



dplyr nse (3)

Imagina que hay una x diferente que quieres multiplicar:

> x <- 3 > f1(d, !!x) x y two_y 1 1 -2.488894875 6 2 2 -1.133517746 6 3 3 -1.024834108 6 4 4 0.730537366 6 5 5 -1.325431756 6

vs sin el !! :

> f1(d, x) x y two_y 1 1 -2.488894875 2 2 2 -1.133517746 4 3 3 -1.024834108 6 4 4 0.730537366 8 5 5 -1.325431756 10

!! le da más control sobre el alcance que sobre el substitute ; con el sustituto, solo puede obtener la 2ª forma fácilmente.

En el siguiente ejemplo, ¿por qué deberíamos favorecer el uso de f1 sobre f2 ? ¿Es más eficiente en algún sentido? Para alguien que solía usar R, parece más natural usar la opción "substituir + eval".

library(dplyr) d = data.frame(x = 1:5, y = rnorm(5)) # using enquo + !! f1 = function(mydata, myvar) { m = enquo(myvar) mydata %>% mutate(two_y = 2 * !!m) } # using substitute + eval f2 = function(mydata, myvar) { m = substitute(myvar) mydata %>% mutate(two_y = 2 * eval(m)) } all.equal(d %>% f1(y), d %>% f2(y)) # TRUE

En otras palabras, y más allá de este ejemplo en particular, mi pregunta es: ¿puedo salir dplyr con la programación usando las funciones dplyr NSE con una buena base ol como sustituto + eval, o realmente necesito aprender a amar todas esas funciones de rlang porque? ¿Hay algún beneficio (velocidad, claridad, composición, ...)?


Quiero dar una respuesta que sea independiente de dplyr , porque hay una ventaja muy clara de usar enquo sobre substitute . Ambos buscan en el entorno de llamada de una función para identificar la expresión que se le dio a esa función. La diferencia es que el substitute() hace solo una vez, mientras que !!enquo() recorrerá correctamente toda la pila de llamadas.

Considere una función simple que usa substitute() :

f <- function( myExpr ) { eval( substitute(myExpr), list(a=2, b=3) ) } f(a+b) # 5 f(a*b) # 6

Esta funcionalidad se interrumpe cuando la llamada se anida dentro de otra función:

g <- function( myExpr ) { val <- f( substitute(myExpr) ) ## Do some stuff val } g(a+b) # myExpr <-- OOPS

Ahora considere las mismas funciones reescritas usando enquo() :

library( rlang ) f2 <- function( myExpr ) { eval_tidy( enquo(myExpr), list(a=2, b=3) ) } g2 <- function( myExpr ) { val <- f2( !!enquo(myExpr) ) val } g2( a+b ) # 5 g2( b/a ) # 1.5

Y es por eso que enquo() + !! Es preferible substitute() + eval() .

dplyr simplemente aprovecha al máximo esta propiedad para crear un conjunto coherente de funciones NSE.


enquo() y !! también le permite programar con otros verbos group_by como group_by y select . No estoy seguro si substitute y eval pueden hacer eso. Eche un vistazo a este ejemplo donde modifico un poco su marco de datos.

library(dplyr) set.seed(1234) d = data.frame(x = c(1, 1, 2, 2, 3), y = rnorm(5), z = runif(5)) # select, group_by & create a new output name based on input supplied my_summarise <- function(df, group_var, select_var) { group_var <- enquo(group_var) select_var <- enquo(select_var) # create new name mean_name <- paste0("mean_", quo_name(select_var)) df %>% select(!!select_var, !!group_var) %>% group_by(!!group_var) %>% summarise(!!mean_name := mean(!!select_var)) } my_summarise(d, x, z) # A tibble: 3 x 2 x mean_z <dbl> <dbl> 1 1. 0.619 2 2. 0.603 3 3. 0.292

Edición: también enquos & !!! Facilita la captura de lista de variables.

# example grouping_vars <- quos(x, y) d %>% group_by(!!!grouping_vars) %>% summarise(mean_z = mean(z)) # A tibble: 5 x 3 # Groups: x [?] x y mean_z <dbl> <dbl> <dbl> 1 1. -1.21 0.694 2 1. 0.277 0.545 3 2. -2.35 0.923 4 2. 1.08 0.283 5 3. 0.429 0.292 # in a function my_summarise2 <- function(df, select_var, ...) { group_var <- enquos(...) select_var <- enquo(select_var) # create new name mean_name <- paste0("mean_", quo_name(select_var)) df %>% select(!!select_var, !!!group_var) %>% group_by(!!!group_var) %>% summarise(!!mean_name := mean(!!select_var)) } my_summarise2(d, z, x, y) # A tibble: 5 x 3 # Groups: x [?] x y mean_z <dbl> <dbl> <dbl> 1 1. -1.21 0.694 2 1. 0.277 0.545 3 2. -2.35 0.923 4 2. 1.08 0.283 5 3. 0.429 0.292

Crédito: Programación con dplyr.