wake-up-neo.com

Datenrahmen nach Zeichenspaltenname filtern (in dplyr)

Ich habe einen Datenrahmen und möchte ihn auf zwei Arten filtern, entweder in der Spalte "this" oder in der Spalte "that". Ich möchte gerne auf den Spaltennamen als Variable verweisen. Wie (in dplyr, wenn das einen Unterschied macht) verweise ich auf einen Spaltennamen durch eine 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

Angenommen, ich möchte die Variable column verwenden, um entweder "this" oder "das" zu speichern und nach dem Wert von column zu filtern. Sowohl as.symbol als auch get funktionieren in anderen Zusammenhängen, nicht jedoch in diesem Zusammenhang:

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

Wie kann ich den Wert von column in einen Spaltennamen umwandeln?

21
William Denton

Aus der aktuellen Dplyr-Hilfedatei (Hervorhebung von mir):

dplyr wird verwendet, um zwei Versionen jedes Verbs mit einem Unterstrich anzubieten. Diese Versionen hatten eine SE-Semantik (Standard Evaluation): Anstatt wie NSE-Verben Argumente nach Code zu nehmen, nahmen sie Argumente nach Wert. Sie sollten es ermöglichen, mit dplyr zu programmieren. Allerdings verwendet dplyr jetzt eine ordentliche Bewertungssemantik . NSE-Verben erfassen immer noch ihre Argumente, aber Sie können jetzt Teile dieser Argumente aus dem Zitat entfernen. Dies bietet vollständige Programmierbarkeit mit NSE-Verben. Somit sind die unterstrichenen Versionen jetzt überflüssig.

Was nicht zitieren genau bedeutet, kann in der Vignette gelernt werden Programmieren mit dplyr . Dies wird durch die Funktion UQ() oder als syntaktischer Zucker durch !! Erreicht. Jetzt gibt es Situationen - wie die Ihren -, in denen nur die ersteren richtig funktionieren, weil !! Mit der Single ! Kollidieren kann.

Angewandt auf Ihr Beispiel:

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

Aber nicht:

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

Der syntaktische Zucker !! Funktioniert wieder wie angenommen, wenn Sie entweder zusätzliche runde Klammern hinzufügen (danke an Martijn vd Voort für den Vorschlag):

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

Oder wenn Sie nur die beiden Vergleichsoperanden vertauschen (danke an carand für den Hinweis):

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

Ich würde vermeiden, get() alle zusammen zu verwenden. Es scheint, als wäre es in dieser Situation ziemlich gefährlich, besonders wenn Sie programmieren. Sie können entweder einen nicht bewerteten Anruf oder eine eingefügte Zeichenfolge verwenden. Sie müssen jedoch filter_() anstelle von filter() verwenden.

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

Option 1 - Verwendung eines nicht bewerteten Anrufs: 

Sie können y als 1 hartcodieren, aber hier zeige ich es als y, um zu veranschaulichen, wie Sie die Ausdruckswerte leicht ändern können.

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 - mit paste() (und natürlich einfacher):

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

Das wichtigste an diesen beiden Optionen ist, dass wir filter_() anstelle von filter() verwenden müssen. Nach dem, was ich gelesen habe, sollten Sie bei der Programmierung mit dplyr immer die Funktionen von *_() verwenden.

Ich habe diesen Beitrag als hilfreiche Referenz verwendet: Zeichenfolge als Funktionsargument r und benutze dplyr Version 0.3.0.2.

23
Rich Scriven

In Bezug auf Richards Lösung möchte ich nur hinzufügen, wenn die Spalte Charakter ist. Sie können shQuote hinzufügen, um nach Zeichenwerten zu filtern.

Zum Beispiel können Sie verwenden

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

Wenn Sie über mehrere Filter verfügen, können Sie collapse = "&" in paste angeben.

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

Hier ist eine andere Lösung für die neueste Dplyr-Version:

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

df %>% filter(.[[column]] == 1)

#  this that
#1    1    1
1
paul_dg

Oder mit filter_at

library(dplyr)
df %>% 
   filter_at(vars(column), any_vars(. == 1))
1
akrun

Wie Salim B oben erklärt, jedoch mit einer geringfügigen Änderung:

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

d. h. einfach die Bedingung umkehren, da !! sich ansonsten __ 

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