web-dev-qa-db-fra.com

Évaluation non standard (NSE) dans le filtre de dplyr et l'extraction des données de MySQL

Je voudrais extraire des données d'un serveur SQL avec un filtre dynamique. J'utilise le grand package R dplyr de la manière suivante:

#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

Ce morceau de code fonctionne bien mais maintenant je voudrais le boucler d'une manière ou d'une autre sur toutes les colonnes de ma table, donc j'aimerais écrire le filtre comme:

#Dynamic filter
i <- 2 #With a loop on this i for instance
which_column <- paste0("column",i)
filter_criteria <- ~ which_column %in% some_vector

Et puis réappliquez le premier code avec le filtre mis à jour.

Malheureusement, cette approche ne donne pas les résultats escomptés. En fait, cela ne donne aucune erreur mais ne tire même aucun résultat dans R. En particulier, j'ai examiné un peu la requête SQL générée par les deux morceaux de code et il y a une différence importante.

Alors que le premier, qui fonctionne, le code génère une requête du formulaire:

SELECT ... FROM ... WHERE 
`column1` IN ....

(`signe dans le nom de la colonne), la seconde génère une requête du formulaire:

SELECT ... FROM ... WHERE 
'column1' IN ....

('signez le nom de la colonne)

Quelqu'un at-il une suggestion sur la façon de formuler la condition de filtrage pour la faire fonctionner?

31
Lorenzo Rossi

Ce n'est pas vraiment lié à SQL. Cet exemple dans R ne fonctionne pas non plus:

df <- data.frame(
     v1 = sample(5, 10, replace = TRUE),
     v2 = sample(5,10, replace = TRUE)
)
df %>% filter_(~ "v1" == 1)

Cela ne fonctionne pas car vous devez passer à filter_ l'expression ~ v1 == 1 - pas l'expression ~ "v1" == 1.

Pour résoudre le problème, utilisez simplement l'opérateur de cotation quo et l'opérateur dequotation !!

library(dplyr)
which_column = quot(v1)
df %>% filter(!!which_column == 1)
43
Matthew

Une solution alternative, avec dplyr version 0.5.0 (probablement implémentée plus tôt que cela), il est possible de passer une chaîne composée comme argument .dots, que je trouve plus lisible que la solution 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

[~ # ~] mise à jour [~ # ~] pour dplyr 0.6 et versions ultérieures:

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)

(Similaire à la réponse de @Matthew pour dplyr 0.6, mais je suppose que which_col est une variable chaîne.)

2ème MISE À JOUR : Edwin Thoen a créé une cheatsheet agréable pour une évaluation ordonnée: https://edwinth.github.io/blog/dplyr-recipes /

8
LmW.

Voici une solution un peu moins détaillée et qui utilise le comportement typique de la fonction d'extraction, '[' en sélectionnant une colonne par valeur de caractère plutôt qu'en la convertissant en élément de langage:

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
5
42-