2014-12-28 36 views
9

考慮評估都列名和內內``j`表達目標值data.table`

target <- "vs" 
value <- 1 

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

所以我想通過這兩個列名和內的值作爲變量進入j表達data.table環境,東西就相當於

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

如果只值的變量,它工作得很好

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

我們也可以撥打data.table範圍內的列時,它是一個變量

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

但我找不出如何兩個簡單的方式

注意結合:我很清楚,我可以簡單地做:

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

但我需要它的數據表範圍之內,所以我可以修改由R等欄目eference,像

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

因此,這裏是我的嘗試時,都列名和值是變量

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... 

最後我想出了

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

,但它是非常低效的,這裏有一個簡單的基準

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 

問題:有沒有更好的方法來做到這一點,我在這裏失蹤?無論是東西更地道或更有效


編輯

本來我一直在尋找一些成語,所以我想用get @GGrothendieck簡單的解決方案是一個,但令人驚訝的所有@Richard版本是甚至超過了該版本ins't做列名的任何評價

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

試試'get(target)' –

+0

這些都讓我看起來很奇怪。 (@ jangorecki的賞金把我帶到了這裏。)爲什麼在行計數器上使用'by'?這不可能是最佳的。爲什麼'總和'一個0/1標量?使用'dt [,mycol:= 0L]; dt [獲取(目標)==值,mycol:= 1L]',可以將相同的矢量快700倍(在我的計算機上)。用'dt [,table(mycol,NEWCOL5)]檢查' – Frank

+0

除了我從Richard的回答中學到了有趣的替換之外,我仍然無法在字符向量輸入中處理類似*通過引用*編程更新。例如:a)'select < - c(「value」); DT [JN,c(「value」):= list(i.value)]'和b)'select < - c(「value」,「meta」); DT [JN,c(「value」,「meta」):= list(i.value,i.meta)'。試着用lapply(select,as.name(paste0(「i。」,select)))'但嵌套在列表中的'name'似乎沒有被捕獲。我可能會爲此提出一個新問題。 – jangorecki

回答

9

這裏是一個可能的選擇。

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

在代碼方面,它不一定簡單,但是我們可以設置的dt範圍是數據表的環境中進行評估外定義的未計算的通話cl

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

substitute()可能需要更長的表達式。但在這種情況下,call()會縮短代碼並創建相同的cl結果。因此cl也可能是

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

現在我們可以評估cldt。在你的例子中,這似乎工作正常。

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 

思考這個一分鐘後,我不知道value需要被取代,因此下面也適用。但正如David所言,第一種方法更省時。

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

我發現這個答案非常有價值,只是爲了準備靈活使用'data.table'的調用。不幸的是,我需要等23小時才能給你一筆獎金。 BTW。類似的方法,而不是'i'參數中的'j' arg:[gist](https://gist.github.com/jangorecki/06a05f0efab256765f53)。 – jangorecki

+0

你可以檢查問題最後的評論嗎?也許這對你來說很簡單... – jangorecki