2014-12-28 5 views
9

beide Spaltennamen und den Zielwert innerhalb 'J' Ausdruck Auswertung innerhalb `data.table`

Betrachten
target <- "vs" 
value <- 1 

library(data.table) 
dt <- as.data.table(head(mtcars)) 

Also innerhalb Ich versuche, beide Spaltennamen übergeben und einen Wert als Variablen in den j Ausdruck data.table Umgebung, etwas, das entspricht

dt[, vs == 1] 
# [1] FALSE FALSE TRUE TRUE FALSE TRUE 

wäre, wenn nur der Wert der Variable, es schön

dt[, vs == value] 
# [1] FALSE FALSE TRUE TRUE FALSE TRUE 
arbeitet

Wir können auch die Spalte im data.table Umfang nennen, wenn es

dt[, target, with = FALSE] 
# vs 
# 1: 0 
# 2: 0 
# 3: 1 
# 4: 1 
# 5: 0 
# 6: 1 

eine Variable ist Aber ich kann nicht verstehen, wie die beiden auf einfache Weise

Hinweis kombinieren: ich bin mir bewusst, dass ich einfach tun:

dt[[target]] == value 
# [1] FALSE FALSE TRUE TRUE FALSE TRUE 

Aber ich brauche es in der Datentabelle Umfang so dass ich andere Spalten von r ändern könnte bersicht, so etwas wie

dt[, NEWCOL := sum(vs == 1), by = am] 

hier So sind meine Versuche, wenn beide Spaltenname und der Wert sind Variablen

dt[, target == value, with = FALSE] 
# Null data.table (0 rows and 0 cols) 
dt[, target == value] 
# [1] FALSE 
dt[, (target) == value] 
# [1] FALSE 
dt[, .(target == value)] 
# V1 
# 1: FALSE 
dt[, eval(target) == value] 
# [1] FALSE 
dt[target %in% value] 
## Empty data.table (0 rows) of 11 cols: mpg,cyl,disp,hp,drat,wt... 

Schließlich kam ich mit

dt[, .SD[[target]] == value] 
# [1] FALSE FALSE TRUE TRUE FALSE TRUE 

aber es ist sehr ineffizient, Hier ist ein einfacher Benchmark

set.seed(123) 
n <- 1e6 
dt <- data.table(vs = sample(1L:30L, n, replace = TRUE), am = seq_len(n)) 
system.time(dt[, NEWCOL := sum(.SD[[target]] == value), by = am]) 
# user system elapsed 
# 13.00 0.02 13.12 
system.time(dt[, NEWCOL2 := sum(vs == value), by = am]) 
# user system elapsed 
# 0.82 0.00 0.83 

Frage: Gibt es einen besseren Weg, dies zu tun, dass ich hier vermisse? Etwas entweder mehr idiomatische oder wesentlich effizienter


bearbeiten

Ursprünglich ich war auf der Suche nach etwas, idiomatisch, so dachte ich @GGrothendieck einfache Lösung mit get war derjenige, aber überraschend alle @ Richard Version sind auch die Version zu schlagen, dass ins't jede Bewertung des Spaltennamen tut

set.seed(123) 
n <- 1e7 
dt <- data.table(vs = sample(1L:30L, n, replace = TRUE), am = seq_len(n)) 

cl <- substitute(
    x == y, 
    list(x = as.name(target), y = value) 
) 
cl2 <- call("==", as.name(target), value) 

system.time(dt[, NEWCOL := sum(vs == value), by = am]) 
# user system elapsed 
# 0.83 0.00 0.82 
system.time(dt[, NEWCOL1 := sum(.SD[[target]] == value), by = am]) 
# user system elapsed 
# 8.97 0.00 8.97 
system.time(dt[, NEWCOL2 := sum(get(target) == value), by = am]) 
# user system elapsed 
# 2.35 0.00 2.37 
system.time(dt[, NEWCOL3 := sum(eval(cl)), by = am]) 
# user system elapsed 
# 0.69 0.02 0.71 
system.time(dt[, NEWCOL4 := sum(eval(cl2)), by = am]) 
# user system elapsed 
# 0.76 0.00 0.77 
system.time(dt[, NEWCOL5 := sum(eval(as.name(target)) == value), by = am]) 
# user system elapsed 
# 0.78 0.00 0.78 
+4

Probieren Sie 'get (target) ' –

+0

Das sehen alle wirklich komisch aus. (@ jangoreckis Bounty hat mich hierher gebracht.) Warum benutzt man 'by' mit einem Zeilenzähler? Das kann unmöglich optimal sein. Und warum 'sum' einen 0/1-Skalar? Derselbe Vektor kann 700x schneller (auf meinem Computer) mit 'dt [, mycol: = 0L]; dt [get (target) == value, mycol: = 1L]' erzeugt werden. Prüfe mit 'dt [, table (mycol, NEWCOL5)]' – Frank

+0

Außerdem habe ich eine interessante Ersetzung von Richards Antwort erfahren. Ich kann immer noch nicht mit ähnlicher * Programmieraktualisierung durch Referenz * auf Zeichenvektoreingabe umgehen. Etwas wie: a) 'select <- c (" value "); DT [JN, c ("Wert"): = list (i.value)] 'und b)' select <- c ("value", "meta"); DT [JN, c ("Wert", "Meta"): = Liste (i.value, i.meta) '. Versucht mit 'lapply (select, as.name (paste0 (" i. ", Select))) aber' names 'nested in Liste scheint nicht eingefangen zu sein. Ich könnte dafür eine neue Frage stellen. – jangorecki

Antwort

9

Hier ist ein mögliche Alternative.

target <- "vs" 
value <- 1 
dt <- as.data.table(head(mtcars)) 

In Bezug auf die Code ist es nicht unbedingt einfacher, aber wir können einen unevaluierten Anruf cl außerhalb des Bereichs der dt definiert eingerichtet, die innerhalb der Datentabelle Umgebung ausgewertet werden soll.

cl <- substitute(
    x == y, 
    list(x = as.name(target), y = value) 
) 

substitute() könnte für längere Ausdrücke erforderlich sein.Aber in diesem Fall würde call() den Code verkürzen und das gleiche cl Ergebnis erzeugen. Und so könnte cl auch sein

cl <- call("==", as.name(target), value) 

Jetzt sind wir cl innen dt auswerten können. Bei Ihrem Beispiel scheint das gut zu funktionieren.

dt[, NEWCOL := sum(eval(cl)), by = am][] 
#  mpg cyl disp hp drat wt qsec vs am gear carb NEWCOL 
# 1: 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4  1 
# 2: 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4  1 
# 3: 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1  1 
# 4: 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1  2 
# 5: 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2  2 
# 6: 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1  2 

Nach einer Minute darüber nachgedacht, bin ich value wechselt wird nicht sicher erforderlich, und damit auch die folgenden Arbeiten. Aber wie David bemerkt, ist der erste Ansatz zeiteffizienter.

dt[, eval(as.name(target)) == value] 
# [1] FALSE FALSE TRUE TRUE FALSE TRUE 
+0

Ich fand diese Antwort sehr wertvoll, nur für die Vorbereitung der Anrufe für die flexible Nutzung von 'data.table'. Leider muss ich 23h warten, bevor ich Ihnen ein Kopfgeld geben kann. Übrigens. Ähnliche Vorgehensweise anstelle von 'j' arg in' i' Argument: [gist] (https://gist.github.com/jangorecki/06a05f0efab256765f53). – jangorecki

+0

Können Sie den letzten Kommentar unter der Frage überprüfen? vielleicht wäre es einfach für dich ... – jangorecki

Verwandte Themen