[英]Fast data.table assign of multiple columns by group from lookup
我一直在尋找規范的方法來做我想做的事情,但是我似乎運氣不高,卻又快又優雅。 簡而言之,我有一個包含多個值列的大型表,並希望將它們分別乘以查找表中的相應因子。 我無法弄清楚如何動態地將要乘以哪些列乘以查找值,或者如何在基本表達式之外一般引用引用值。
這是我的示例,我將其設置為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.value_1
之類的i列,因此我可以將它們傳遞到循環中,或者更好地一次應用它們。
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:用eval(parse(text=col))
代替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
編輯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 ]
如果您的數據集太大,並且您想查看操作的進度條,則可以使用pblapply
:
library(pbapply)
random[lookup, (cols) := pblapply(cols, function(x) get(x) * get(paste0("i.", x))), by = .EACHI ]
這比文本解析/調用構造慢大約2倍,但可讀性更高:
random[lookup, (wps) := Map('*', mget(wps), mget(paste0('i.', wps))), by = .EACHI]
感謝jangorecki在這里指出他的答案,后者使用助手函數動態構建J表達式,然后立即求值。 它避免了解析/獲取的開銷,並且似乎是我要獲得的最快的解決方案。 我還喜歡手動指定要調用的函數(某些情況下,我可能需要/
而不是*
)並在評估J表達式之前對其進行檢查的功能。
batch.lookup = function(x) {
as.call(list(as.name(":="),x
,as.call(c(
list(as.name("list")),
sapply(x, function(x) call("*", as.name(x), as.name(paste0("i.",x))), simplify=FALSE)
))
))
}
print(batch.lookup(wps))
`:=`(c("value_1", "value_2", "value_3", "value_4", "value_5",
"value_6", "value_7", "value_8", "value_9", "value_10"), list(value_1 = value_1 *
i.value_1, value_2 = value_2 * i.value_2, value_3 = value_3 *
i.value_3, value_4 = value_4 * i.value_4, value_5 = value_5 *
i.value_5, value_6 = value_6 * i.value_6, value_7 = value_7 *
i.value_7, value_8 = value_8 * i.value_8, value_9 = value_9 *
i.value_9, value_10 = value_10 * i.value_10))
system.time(
random[lookup, eval(batch.lookup(wps)), by = .EACHI])
user system elapsed
0.14 0.04 0.18
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.