考虑
target <- "vs"
value <- 1
library(data.table)
dt <- as.data.table(head(mtcars))
Run Code Online (Sandbox Code Playgroud)
所以我试图将列名和值作为变量传递到环境中的j表达式中data.table,这相当于
dt[, vs == 1]
# [1] FALSE FALSE TRUE TRUE FALSE TRUE
Run Code Online (Sandbox Code Playgroud)
如果只有值是变量,它可以很好地工作
dt[, vs == value]
# [1] FALSE FALSE TRUE TRUE FALSE TRUE
Run Code Online (Sandbox Code Playgroud)
当它是变量时,我们也可以在data.table范围内调用该列
dt[, target, with = FALSE]
# vs
# 1: 0
# 2: 0
# 3: 1
# 4: 1
# 5: 0
# 6: 1
Run Code Online (Sandbox Code Playgroud)
但我无法想象如何以简单的方式将两者结合起来
注意:我很清楚我可以做到:
dt[[target]] == value
# [1] FALSE FALSE TRUE TRUE FALSE TRUE
Run Code Online (Sandbox Code Playgroud)
但我需要在数据表范围内,所以我可以通过引用修改其他列,如
dt[, NEWCOL := sum(vs == 1), by = am]
Run Code Online (Sandbox Code Playgroud)
所以这里是我的尝试,当列名和值都是变量
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...
Run Code Online (Sandbox Code Playgroud)
最终我想到了
dt[, .SD[[target]] == value]
# [1] FALSE FALSE TRUE TRUE FALSE TRUE
Run Code Online (Sandbox Code Playgroud)
但这是非常低效的,这是一个简单的基准
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
Run Code Online (Sandbox Code Playgroud)
问题:有没有更好的办法让我在这里失踪?一些更惯用或更高效的东西
编辑
最初我一直在寻找一些惯用的东西,所以我认为@GGrothendieck使用的简单解决方案get是一个,但令人惊讶的是所有@Richard版本都击败了那些没有对列名做任何评估的版本
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
Run Code Online (Sandbox Code Playgroud)
这是一种可能的替代方案.
target <- "vs"
value <- 1
dt <- as.data.table(head(mtcars))
Run Code Online (Sandbox Code Playgroud)
在代码方面,它不一定更简单,但我们可以设置一个在数据表环境中评估cl范围之外定义的未dt评估调用.
cl <- substitute(
x == y,
list(x = as.name(target), y = value)
)
Run Code Online (Sandbox Code Playgroud)
substitute()可能需要更长的表达式.但在这种情况下,call()会缩短代码并创建相同的cl结果.所以cl也可能
cl <- call("==", as.name(target), value)
Run Code Online (Sandbox Code Playgroud)
现在我们可以评估cl内部dt.在你的例子中,这似乎工作正常.
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
Run Code Online (Sandbox Code Playgroud)
在考虑了这一点之后,我不确定是否value需要替换,因此以下也有效.但正如大卫所说,第一种方法更有时间效率.
dt[, eval(as.name(target)) == value]
# [1] FALSE FALSE TRUE TRUE FALSE TRUE
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
768 次 |
| 最近记录: |