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?
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
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.
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 = "&"))
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)