在R中快速替代split函数的方法

14

我使用split()对一个包含1.3百万行和20列的数据帧进行分区,以便使用parLapply()并行调用每个分区中的函数。我按两列分割/分区,它们都是字符类型。看起来有大约47K个唯一ID和12K个唯一代码,但不是每个ID和代码的组合都匹配。得到的分区数大约为250K。以下是split()的行:

 system.time(pop_part <- split(pop, list(pop$ID, pop$code)))
分区将按以下方式传送到parLapply()中:
cl <- makeCluster(detectCores())
system.time(par_pop <- parLapply(cl, pop_part, func))
stopCluster(cl)

我让split()的代码运行了将近一个小时,但仍未完成。 我可以仅按ID拆分,大约需要10分钟时间。 另外,R Studio和工作线程正在消耗大约6GB的RAM。

我知道分区的数量是因为我在Pentaho Data Integration(PDI)中有等效的代码,该程序在30秒内运行(针对整个程序,而不仅仅是“split”代码)。 我并不指望R能够达到那种性能,但希望它最坏情况下能够在10-15分钟内完成。

主要问题:是否有更好的拆分替代方案? 我还尝试过ddply().parallel = TRUE,但它也运行了一个多小时,并且从未完成。

2个回答

13

将索引拆分为pop

idx <- split(seq_len(nrow(pop)), list(pop$ID, pop$code))

Split 不慢,例如,

> system.time(split(seq_len(1300000), sample(250000, 1300000, TRUE)))
   user  system elapsed 
  1.056   0.000   1.058 

如果你的数据很慢,那么我猜测有一些方面会影响它的速度,例如,IDcode都是拥有许多级别的因素,因此会计算它们的完整交互,而不是数据集中出现的级别组合。

> length(split(1:10, list(factor(1:10), factor(10:1))))
[1] 100
> length(split(1:10, paste(letters[1:10], letters[1:10], sep="-")))
[1] 10

或许您的内存不足了。

如果您正在使用非Windows机器(我猜想这是您要求 detectCores() 的原因),请使用mclapply而不是parLapply来使用进程。

par_pop <- mclapply(idx, function(i, pop, fun) fun(pop[i,]), pop, func)

从概念上来看,你似乎真正的目标是 pvec(在处理器上分布向量化计算),而不是 mclapply(在数据框的各个行上迭代)。

此外,作为最初的步骤,请考虑确定func中的瓶颈;数据很大但并不那么大,因此也许不需要并行评估——也许你编写了PDI代码而不是R代码?注意数据框中的数据类型,例如因子与字符。通常情况下,效率低下和高效的R代码之间可以获得100倍的加速,而并行评估最多只能与核心数成正比。


谢谢,我会试试看。哈,其实我最初是写了R代码,然后将其移植到PDI(因为我对R比PDI更有经验)。 - argoneus
我运行了你发布的split()代码,等了将近一个小时,但它从未完成。 - argoneus
在split周围添加了一些额外的建议,这应该只需要大约一秒钟的时间。也许因素也导致func变慢了? - Martin Morgan
这对我非常非常有帮助。我长时间以来一直盲目地在数据框上使用"split"函数,用粘贴的字符串进行操作,而且内存开销非常大(数据框将RAM乘以4倍以上)。这使得我能够在9个核心上运行我的代码,而不是2个,非常感谢。 - Rguy

5

如果x是一个因子且f包含许多不同的元素,那么Split(x,f)将变得很慢。

因此,这段代码很快:

system.time(split(seq_len(1300000), sample(250000, 1300000, TRUE)))

但是,这样会非常慢:

system.time(split(factor(seq_len(1300000)), sample(250000, 1300000, TRUE)))

这很快,因为只有25个组。

system.time(split(factor(seq_len(1300000)), sample(25, 1300000, TRUE)))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接