我一直在寻找规范的方法来完成我正在尝试的事情,但我似乎不太幸运地获得快速而优雅的工作。简而言之,我有一个包含多个值列的大表,并且希望将每个值乘以查找表中的相应因子。我不知道如何动态传递我想要乘以查找值的列,或者如何在基本表达式之外引用查找值。
这是我的示例,我将其设置为 300 万行和 10 个值列,这不会花费太长时间,并且在某种程度上代表了数据大小(这将作为更大循环的一部分来实现,因此强调关于性能)。还有一个包含 6 个级别的查找表以及一些用于 value_1:value_10 列的分类乘数。
library(data.table)
setsize <- 3000000
value_num <- 10
factors <- c("factor_a", "factor_b", "factor_c", "factor_d", "factor_e", "factor_f")
random <- data.table(replicate(10, sample(factors, size = setsize, replace = T))
, replicate(10, rnorm(setsize, mean = 700, sd = 50)))
lookup <- data.table("V1" = factors, replicate(10, seq(.90, 1.5, length.out = length(factors))))
wps <- paste("value", c(1:10), sep = "_")
names(random)[11:20] <- wps
names(lookup)[2:11] <- wps
setkeyv(random, "V1")
setkeyv(lookup, "V1")
解决方案 1:速度相当快,但我不知道如何通用地引用 i 列,例如i.value_1
所以我可以将它们传递到一个循环中,或者更好的是一次性应用它们。
f <- function() {
random[lookup, value_1 := value_1 * i.value_1, by = .EACHI]
random[lookup, value_2 := value_2 * i.value_2, by = .EACHI]
random[lookup, value_3 := value_3 * i.value_3, by = .EACHI]
random[lookup, value_4 := value_4 * i.value_4, by = .EACHI]
random[lookup, value_5 := value_5 * i.value_5, by = .EACHI]
random[lookup, value_6 := value_6 * i.value_6, by = .EACHI]
random[lookup, value_7 := value_7 * i.value_7, by = .EACHI]
random[lookup, value_8 := value_8 * i.value_8, by = .EACHI]
random[lookup, value_9 := value_9 * i.value_9, by = .EACHI]
random[lookup, value_10 := value_10 * i.value_10, by = .EACHI]
}
system.time(f())
user system elapsed
0.184 0.000 0.181
解决方案 2:在我无法使解决方案 1 通用后,我尝试了set()
基于的方法。然而,尽管允许我在字符向量中指定目标值列wps
,实际上比上面慢很多。我知道我用错了它,但不确定如何改进它以消除所有 [.data.table 开销。
idx_groups <- random[,.(rowstart = min(.I), rowend = max(.I)), by = key(random)][lookup]
system.time(
for (i in 1:nrow(idx_groups)){
rows <- idx_groups[["rowstart"]][i]:idx_groups[["rowend"]][i]
for (j in wps) {
set(random, i=rows, j=j, value= random[rows][[j]] * idx_groups[[j]][i])
}
})
user system elapsed
3.940 0.024 3.967
任何有关如何更好地构建这些操作的建议将不胜感激。
编辑:我对自己感到非常沮丧,因为在发布这个问题之前未能尝试这个明显的解决方案:
system.time(
for (col in wps){
random[lookup, (col) := list(get(col) * get(paste0("i.", col))), by = .EACHI, with = F]
})
user system elapsed
1.600 0.048 1.652
这似乎以相对速度做我想做的事。然而它仍然比上面第一个解决方案慢 10 倍(我确信由于重复get()
)所以我仍然愿意接受建议。
编辑2:替换get()
with eval(parse(text=col))
似乎已经成功了。
system.time(
for (col in wps){
random[lookup, (col) := list(eval(parse(text=col)) * eval(parse(text=paste0("i.", col)))), by = .EACHI, with = F]
})
user system elapsed
0.184 0.000 0.185
编辑3:已经提供了几个很好的工作答案。 Rafael 的解决方案在一般情况下可能是最好的,尽管我会注意到,我可以从 Jangorecki 推荐的调用构造中多挤出几毫秒,以换取看起来相当吓人的辅助函数。我已将其标记为已回答,谢谢大家的帮助。