web-dev-qa-db-fra.com

Filtrer le cadre de données par nom de colonne de caractère (en dplyr)

J'ai un cadre de données et je souhaite le filtrer de deux manières différentes: colonne "ceci" ou colonne "cela". J'aimerais pouvoir faire référence au nom de la colonne en tant que variable. Comment (dans dplyr, si cela fait une différence) faire référence à un nom de colonne par une variable?

library(dplyr)
df <- data.frame(this = c(1, 2, 2), that = c(1, 1, 2))
df
#   this that
# 1    1    1
# 2    2    1
# 3    2    2
df %>% filter(this == 1)
#   this that
# 1    1    1

Mais disons que je veux utiliser la variable column pour contenir "ceci" ou "cela" et filtrer quelle que soit la valeur de column. as.symbol et get fonctionnent tous les deux dans d'autres contextes, mais pas ceci:

column <- "this"
df %>% filter(as.symbol(column) == 1)
# [1] this that
# <0 rows> (or 0-length row.names)
df %>% filter(get(column) == 1)
# Error in get("this") : object 'this' not found

Comment puis-je transformer la valeur de column dans un nom de colonne?

21
William Denton

Dans le fichier d’aide actuel de Dplyr (souligné par moi):

dplyr proposait des versions jumelles de chaque verbe suffixé par un soulignement. Ces versions avaient une sémantique standard d'évaluation (SE): plutôt que de prendre des arguments par code, comme les verbes NSE, elles prenaient des arguments par valeur. Leur but était de permettre de programmer avec dplyr. Cependant, dplyr utilise maintenant la sémantique d’évaluation. Les verbes NSE capturent toujours leurs arguments, mais vous pouvez maintenant en citer une partie. Ceci offre une programmabilité complète avec les verbes NSE. Ainsi, les versions soulignées sont maintenant superflues.

Ce que sans citer signifie exactement peut être appris dans la vignette Programmation avec dplyr . Il est obtenu par la fonction UQ() ou sous la forme sucre syntaxique de !!. Maintenant, il existe des situations - comme la vôtre - où seul l'ancien fonctionne correctement car !! peut entrer en collision avec le ! unique.

Appliqué à votre exemple:

library(dplyr)
df <- data.frame(this = c(1, 2, 2),
                 that = c(1, 1, 2))
column <- "this"

df %>% filter(UQ(as.name(column)) == 1)
#   this that
# 1    1    1

Mais pas:

df %>% filter(!!as.name(column) == 1)
# [1] this that
# <0 Zeilen> (oder row.names mit Länge 0)

Le sucre syntaxique !! fonctionne à nouveau comme supposé si vous ajoutez des crochets ronds supplémentaires (grâce à Martijn vd Voort pour la suggestion):

df %>% filter((!!as.name(column)) == 1)
#   this that
# 1    1    1

Ou si vous échangez simplement les deux opérandes de comparaison (grâce à carand pour le conseil):

df %>% filter(1 == !!as.name(column))
#   this that
# 1    1    1
19
Salim B

Je voudrais éviter d'utiliser get() tous ensemble. Il semble que ce serait très dangereux dans cette situation, surtout si vous programmez. Vous pouvez utiliser un appel non évalué ou une chaîne de caractères collée, mais vous devrez utiliser filter_() au lieu de filter().

df <- data.frame(this = c(1, 2, 2), that = c(1, 1, 2))
column <- "this"

Option 1 - en utilisant un appel non évalué: 

Vous pouvez coder y en tant que 1, mais ici, je le montre en tant que y pour illustrer comment vous pouvez modifier facilement les valeurs d'expression.

expr <- lazyeval::interp(quote(x == y), x = as.name(column), y = 1)
## or 
## expr <- substitute(x == y, list(x = as.name(column), y = 1))
df %>% filter_(expr)
#   this that
# 1    1    1

Option 2 - en utilisant paste() (et évidemment plus facile):

df %>% filter_(paste(column, "==", 1))
#   this that
# 1    1    1

La principale chose à propos de ces deux options est que nous devons utiliser filter_() au lieu de filter(). En fait, d'après ce que j'ai lu, si vous programmez avec dplyr, vous devriez toujours utiliser les fonctions *_().

J'ai utilisé ce message comme référence utile: chaîne de caractères comme argument de fonction r , et j'utilise dplyr version 0.3.0.2.

23
Rich Scriven

Concernant la solution de Richard, je veux juste ajouter que si vous la colonne est un caractère. Vous pouvez ajouter shQuote pour filtrer par valeur de caractère.

Par exemple, vous pouvez utiliser

df %>% filter_(paste(column, "==", shQuote("a")))

Si vous avez plusieurs filtres, vous pouvez spécifier collapse = "&" dans paste.

df %>$ filter_(paste(c("column1","column2"), "==", shQuote(c("a","b")), collapse = "&"))
7
StatCC

Comme Salim B expliqué ci-dessus mais avec un changement mineur:

df %>% filter(1 == !!as.name(column))

c'est-à-dire qu'il suffit d'inverser la condition car !! se comporte sinon. comme. 

!!(as.name(column)==1)
0
carand