我已经搜索了规范的方法来做我正在尝试的东西,但我似乎没有运气得到快速和优雅的工作.简而言之,我有一个包含多个值列的大表,并希望将每个值乘以查找表中的相应因子.我无法弄清楚如何动态传递我想要的列乘以查找值,或者如何在基本表达式之外引用查询值.
这是我的例子,我设置了300万行,有10个值列,这不需要太长时间,并且有点代表数据大小(这将作为更大的循环的一部分实现,因此强调关于表现).我们的value_1:value_10列还有一个包含6个级别和一些匹配乘数的查找表.
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")
Run Code Online (Sandbox Code Playgroud)
解决方案1:它相当快,但我无法弄清楚如何一般地引用i-columns,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
Run Code Online (Sandbox Code Playgroud)
解决方案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
Run Code Online (Sandbox Code Playgroud)
任何有关如何更好地构建这些操作的建议都将受到赞赏.
编辑:在发布此问题之前,我对自己未能尝试这个明显的解决方案感到非常沮丧:
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
Run Code Online (Sandbox Code Playgroud)
这似乎以相对速度做我想要的.然而,它仍然比上面的第一个解决方案慢10倍(我确信由于重复get())所以我仍然愿意接受建议.
编辑2:更换get()同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
Run Code Online (Sandbox Code Playgroud)
编辑3:提供了几个好的工作答案.Rafael的解决方案在一般情况下可能是最好的,但我会注意到我可以从Jangorecki推荐的呼叫结构中挤出几毫秒来换取相当令人生畏的辅助功能.我已将其标记为已回答,感谢大家的帮助.
您还可以使用lapply:
cols <- noquote(paste0("value_",1:10))
random[lookup, (cols) := lapply (cols, function(x) get(x) * get(paste0("i.", x))), by = .EACHI ]
Run Code Online (Sandbox Code Playgroud)
如果您的数据集太大并且您想查看操作的进度条,您可以使用pblapply:
library(pbapply)
random[lookup, (cols) := pblapply(cols, function(x) get(x) * get(paste0("i.", x))), by = .EACHI ]
Run Code Online (Sandbox Code Playgroud)