快速data.table从查找分组多列

etr*_*ler 5 r data.table

我已经搜索了规范的方法来做我正在尝试的东西,但我似乎没有运气得到快速和优雅的工作.简而言之,我有一个包含多个值列的大表,并希望将每个值乘以查找表中的相应因子.我无法弄清楚如何动态传递我想要的列乘以查找值,或者如何在基本表达式之外引用查询值.

这是我的例子,我设置了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推荐的呼叫结构中挤出几毫秒来换取相当令人生畏的辅助功能.我已将其标记为已回答,感谢大家的帮助.

raf*_*ira 4

您还可以使用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)