Puk*_*uki 5 loops r data.table
我有一个关于如何优化以下代码的基本问题.这是我的代码的一个非常简短的版本.基本上,我有一个大的data.table(> 50M行),我想经常对数据进行子集化(比如10000次)并在子集上运行一些函数(显然比下面的例子中显示的更复杂,即我需要子集的所有列,函数返回一个新的data.table).我只是选择了平均值来使示例变得简单.
dt <- data.table(a=sample(letters, 1000000,replace=T),b=sample(1:100000))
mm <- list()
foo <- function(x) mean(x$b)
for(i in 1:1000)
{
mm[[i]] <- foo(dt[a %in% sample(letters,5)])
}
Run Code Online (Sandbox Code Playgroud)
很明显,即使这个最小的例子(设置键等),这也不是编程的最快方法.
但是,我感兴趣的是如何优化for循环.我想到了为子集创建索引然后使用data.table dt[,foo(.SD),by=subset_ID],但我不知道如何做到这一点,因为我正在使用替换(多个组ID)进行采样.任何基于data.table的想法都会非常感激(例如如何删除循环?).
我打算为子集创建索引,然后使用 data.table
dt[,foo(.SD),by=subset_ID],但我不确定如何执行此操作,因为我正在使用替换(多个组 ID)进行采样。
通过连接,您可以拥有重叠的组:
# convert to numeric
dt[, b := as.numeric(b)]
# make samples
set.seed(1)
mDT = setDT(melt(replicate(1000, sample(letters,5))))
setnames(mDT, c("seqi", "g", "a"))
# compute function on each sample
dt[mDT, on=.(a), allow.cartesian=TRUE, .(g, b)][, .(res = mean(b)), by=g]
Run Code Online (Sandbox Code Playgroud)
这使
g res
1: 1 50017.85
2: 2 49980.03
3: 3 50093.80
4: 4 50087.67
5: 5 49990.83
---
996: 996 50013.11
997: 997 50095.43
998: 998 49913.61
999: 999 50058.44
1000: 1000 49909.36
Run Code Online (Sandbox Code Playgroud)
要确认它正在做正确的事情,您可以检查例如,
dt[a %in% mDT[g == 1, a], mean(b)]
# [1] 50017.85
Run Code Online (Sandbox Code Playgroud)
这种方法的一个缺点是它涉及创建一个非常大的表(包含所有样本的数据),这可能会给您带来 RAM 方面的麻烦。
这种方法利用了您的函数mean,因为显式传递它可以进行某些优化;请参阅?GForce,这也是我转换b为数字的原因。
我同意 Rob Jensen 的建议,将列传递给函数而不是传递表(函数对表中出现的列进行假设),这既提高了效率,又提高了清晰度。
在取平均值的具体情况下,您可以通过首先对每个字母进行相加来进一步加快速度,我认为:
dtagg = dt[, .(.N, sumb = sum(b)), by=a]
dtagg[mDT, on=.(a), .(g, sumb, N)][, lapply(.SD, sum), by=g][, .(g, res = sumb/N)]
g res
1: 1 50017.85
2: 2 49980.03
3: 3 50093.80
4: 4 50087.67
5: 5 49990.83
---
996: 996 50013.11
997: 997 50095.43
998: 998 49913.61
999: 999 50058.44
1000: 1000 49909.36
Run Code Online (Sandbox Code Playgroud)
allow.cartesian在这种情况下不需要,因为 的每一行mDT仅在 中找到一行dtagg。在我的计算机上,示例数据的加速相当大,但大部分好处来自于利用示例函数的形式,我猜: