通过R中的数据子集执行计算

时间:2012-06-26 15:11:48

标签: r foreach subset

我想对我的数据框的PERMNO列中的每个公司编号执行计算,其摘要可以在这里看到:

> summary(companydataRETS)
     PERMNO           RET           
 Min.   :10000   Min.   :-0.971698  
 1st Qu.:32716   1st Qu.:-0.011905  
 Median :61735   Median : 0.000000  
 Mean   :56788   Mean   : 0.000799  
 3rd Qu.:80280   3rd Qu.: 0.010989  
 Max.   :93436   Max.   :19.000000  

到目前为止,我的解决方案是创建一个包含所有可能公司编号的变量

compns <- companydataRETS[!duplicated(companydataRETS[,"PERMNO"]),"PERMNO"]

然后使用并行计算的foreach循环调用我的函数get.rho(),然后执行所需的计算

rhos <- foreach (i=1:length(compns), .combine=rbind) %dopar% 
      get.rho(subset(companydataRETS[,"RET"],companydataRETS$PERMNO == compns[i]))

我测试了它的一部分数据,一切正常。问题是我有7200万观察,即使在电脑一夜之间工作,它仍然没有完成。

我是R的新手,所以我想我的代码结构可以改进,并且有一个更好的(更快,更少计算密集)的方式来执行同样的任务(也许使用apply或with,我都不知道明白)。有什么建议吗?

2 个答案:

答案 0 :(得分:3)

根据Joran的建议,我查看了图书馆data.table。对代码的修改是

library(data.table) 
companydataRETS <- data.table(companydataRETS)
setkey(companydataRETS,PERMNO)

rhos <- foreach (i=1:length(compns), .combine=rbind) %do% 
      get.rho(companydataRETS[J(compns[i])]$RET)

我按原样运行代码(使用subset)并使用data.table运行代码,变量compns仅包含数据集中28659家公司中的30家。以下是两个版本system.time()的输出:

使用subset

  

用户........系统.....已经过了    43.925 ... 12.413 ...... 56.337

使用data.table

  

用户.......系统.....已经过了     0.229 ..... 0.047 ....... 0.276

(由于某些原因,使用%do%代替%dopar%原始代码使其运行得更快。system.time() subset是使用%do%的{​​{1}} ,在这种情况下两者中的速度越快。)

我让原始代码一夜之间运行,并且在5小时后它还没有完成,所以我放弃并杀死了它。通过这个小修改,我在不到5分钟的时间里得到了我的结果(我想约3分钟)!

修改

有一种更简单的方法可以使用data.table,而不使用foreach,这涉及替换上面代码的最后一行

rhos <- companydataRETS[ , get.rho(RET), by=PERMNO]

答案 1 :(得分:0)

有很多方法可以做这样的事情,而你的foreach解决方案就是其中之一。只看你提供的代码,我只能猜出最合适的解决方案......

但是,我认为代码中最大的减速实际上是你的get.rho函数而不是循环或子集。如果你想分享这个功能,我打赌你会得到一些惊人的答案,既可以加快速度,也可以澄清一些“R-isms”。

话虽如此,还有许多替代方法可以做你正在做的事情。

plyr包是为这种类型的计算量身定制的。它使用split-apply-combine策略。函数的前两个字母表示输入和输出数据类型。

由于您正在输入data.frame并输出data.frame,因此ddply是要使用的函数:

library(plyr)
ddply(companydataRETS, .(PERMNO), summarise, get.rho(RET))

如果你不在Windows上,你可以使用

轻松地多线程化这个计算
library(doMC)
registerDoMC()
ddply(companydataRETS, .(PERMNO), summarise, get.rho(RET), .parallel=TRUE)

tapply也是一个完美的候选人:

tapply(companydataRETS$RET, companydataRET$PERMNO, get.rho)

评论中提到的data.table软件包也非常出色,但我会将这些代码作为练习留给读者。

然而正如我上面所说,如果你的get.rho功能很慢,无论你的子集和循环技术多么聪明,计算都需要很长时间。


在帖子中编辑功能代码:

如果这是时间序列数据或可以这样处理的数据,则有许多包和函数可以进行这种滞后比较。我对它们并不十分了解,但仔细阅读谷歌和CRAN任务视图将为您提供一个很好的选择概述。

我没有对它进行基准测试,但我认为可以安全地假设代码中最慢的部分是lm调用。在sample数据而非全套数据上执行此操作会大大加快速度。但我相信那里的人会有更好更完整的解决方案。