Evaluación no estándar(NSE) en dplyr''s filter_ & pulling data from MySQL
lazy-evaluation (3)
Me gustaría obtener algunos datos de un servidor sql con un filtro dinámico. Estoy usando el gran paquete R dplyr de la siguiente manera:
#Create the filter
filter_criteria = ~ column1 %in% some_vector
#Connect to the database
connection <- src_mysql(dbname <- "mydbname",
user <- "myusername",
password <- "mypwd",
host <- "myhost")
#Get data
data <- connection %>%
tbl("mytable") %>% #Specify which table
filter_(.dots = filter_criteria) %>% #non standard evaluation filter
collect() #Pull data
Esta pieza de código funciona bien, pero ahora me gustaría bifurcarla de alguna manera en todas las columnas de mi tabla, así que me gustaría escribir el filtro como:
#Dynamic filter
i <- 2 #With a loop on this i for instance
which_column <- paste0("column",i)
filter_criteria <- ~ which_column %in% some_vector
Y luego vuelva a aplicar el primer código con el filtro actualizado.
Lamentablemente, este enfoque no da los resultados esperados. De hecho, no da ningún error, pero ni siquiera extrae ningún resultado en R. En particular, analicé un poco la consulta SQL generada por los dos fragmentos de código y existe una diferencia importante.
Mientras que el primer código que funciona genera una consulta del formulario:
SELECT ... FROM ... WHERE
`column1` IN ....
(`ingrese el nombre de la columna), el segundo genera una consulta del formulario:
SELECT ... FROM ... WHERE
''column1'' IN ....
(''ingrese el nombre de la columna)
¿Alguien tiene alguna sugerencia sobre cómo formular la condición de filtrado para que funcione?
Aquí hay una solución ligeramente menos detallada y una que usa el comportamiento típico de la función de extracción, ''[''
al seleccionar una columna por valor de carácter en lugar de convertirla en un elemento de idioma:
df %>% filter(., ''[''(., which_column)==1 )
set.seed(123)
df <- data.frame(
v1 = sample(5, 10, replace = TRUE),
v2 = sample(5,10, replace = TRUE)
)
which_column <- "v1"
df %>% filter(., ''[''(., which_column)==1)
# v1 v2
#1 1 5
No está realmente relacionado con SQL. Este ejemplo en R tampoco funciona:
df <- data.frame(
v1 = sample(5, 10, replace = TRUE),
v2 = sample(5,10, replace = TRUE)
)
df %>% filter_(~ "v1" == 1)
No funciona porque necesita pasar para filter_
la expresión ~ v1 == 1
- no la expresión ~ "v1" == 1
.
Versión dplyr> = 0.6
Para resolver el problema, simplemente use el operador quoting y el operador de dequoting.
library(dplyr)
which_column = quot(v1)
df %>% filter(!!which_column == 1)
Versión dplyr <0.6
Para resolver el problema, use la función interp
del paquete lazyeval.
library(lazyeval)
filter_criteria <- interp(~ which_column == 1, which_column = as.name("v1"))
df %>% filter_(filter_criteria)
Una solución alternativa, con la versión 0.5.0 de dplyr (probablemente implementada antes que eso), es posible pasar una cadena compuesta como el argumento .dots, que me parece más legible que la solución lazyeval :: interp:
df <- data.frame(
v1 = sample(5, 10, replace = TRUE),
v2 = sample(5,10, replace = TRUE)
)
which_col <- "v1"
which_val <- 1
df %>% filter_(.dots= paste0(which_col, "== ", which_val))
v1 v2
1 1 1
2 1 2
3 1 4
ACTUALIZACIÓN para dplyr 0.6:
packageVersion("dplyr")
# [1] ‘0.5.0.9004’
df %>% filter(UQ(rlang::sym(which_col))==which_val)
#OR
df %>% filter((!!rlang::sym(which_col))==which_val)
(Similar a la respuesta de @Matthew para dplyr 0.6, pero asumo que which_col es una variable de cadena.)