【发布时间】:2017-02-04 19:05:48
【问题描述】:
考虑
target <- "vs"
value <- 1
library(data.table)
dt <- as.data.table(head(mtcars))
所以我试图将列名和值作为变量传递到data.table 环境中的j 表达式,这相当于
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
但我需要在数据表范围内使用它,因此我可以通过引用修改其他列,例如
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
【问题讨论】:
-
这些对我来说真的很奇怪。 (@jangorecki 的赏金将我带到了这里。)为什么将
by与行计数器一起使用?这不可能是最佳的。为什么sum是 0/1 标量?使用dt[,mycol:=0L];dt[get(target)==value,mycol:=1L]可以更快地生成相同的向量(在我的计算机上)700 倍。检查dt[,table(mycol,NEWCOL5)] -
除了我从 Richard 的回答中学到了有趣的替换,我仍然无法处理字符向量输入上类似的 programming update by reference。类似于: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))),但嵌套在列表中的names 似乎没有被捕获。我可能会为此提出一个新问题。
标签: r data.table