【问题标题】:Performing calculations by subsets of data in R通过 R 中的数据子集执行计算
【发布时间】:2012-06-26 15:11:47
【问题描述】:

我想对我的数据框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,这两者我都没有'不懂)。有什么建议吗?

【问题讨论】:

  • 强制注释引用data.table 包。
  • 对您的主要问题没有很好的答案,但您的唯一公司名称列表可能更容易获得 unique(companydataRETS$PERMNO)
  • @joran 我会看看 data.table 包。
  • @joran,我安装了 data.table 并修改了代码以使用它,system.time() 的结果从使用 foreach 的 (43.925 12.413 56.337) 减少到使用数据的 (0.229 0.047 0.276) 。桌子。太不可思议了!真的是我想要的。您认为您可以发布我的问题的答案吗?我可以稍后通过代码修改来完成它,但我想给你点...
  • 对不起,我的意思是使用子集与 data.table。

标签: r foreach subset


【解决方案1】:

按照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% 使其运行得更快。subsetsystem.time() 是使用%do% 的那个,在这种情况下两者中的速度更快。 )

我让原始代码运行了一夜,5 小时后仍未完成,所以我放弃并杀死了它。通过这个小修改,我在不到 5 分钟(我认为大约 3 分钟)内得到了结果!

编辑

还有一种更简单的方法是使用data.table,而不使用foreach,其中包括将上面代码的最后一行替换为

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

【讨论】:

  • 很高兴它成功了。如果您可以提供您不理解的冲突的更多详细信息(例如错误消息),那么我们可以提供帮助。请先检查最新的直播NEWS(也链接在?data.table 的顶部),看看问题是否已经报告并修复。否则,请就此提出一个新问题。它不会对我敲响任何铃声。
  • @MatthewDowle ,错误消息与我之前在使用 source() 加载我的一些函数后尝试使用库 doMC 时遇到的错误相同。我现在尝试重现该错误,但没有发生。这可能与data.table 无关。我将在上面编辑我的答案以删除评论,如果再次发生,我将发布另一个问题。顺便说一句,我做了你上面建议的疯狂的事情。
  • %do%%dopar% 的速度取决于您的并行后端是什么,我认为 - 并行运行具有拆分数据、将其分配给不同节点的所有开销,并重新组合。如果您没有很多用于并行处理的节点,或者您应用的函数非常简单,那么它有时会比 %do% 慢。
【解决方案2】:

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

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

话虽如此,除了做你正在做的事情之外,还有很多选择。

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

由于您输入的是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)

在 cmets 中提到的 data.table 包在这方面也很出色,但我将把该代码作为练习留给读者。

但是,正如我上面所说,如果您的 get.rho 函数很慢,那么无论您使用子集和循环技术多么聪明,计算都将花费很长时间。


在帖子中编辑功能代码:

如果这是时间序列数据,或者可以这样处理的数据,有很多包和函数可以进行这种滞后比较。我对它们不是很精通,但是快速浏览一下 google 和 CRAN 任务视图会让你对你的选择有一个很好的概述。

我还没有对其进行基准测试,但我认为可以安全地假设您的代码中最慢的部分在 lm 调用中。对您的数据的sample 而不是完整数据集执行此操作将大大加快速度。但我敢肯定,有人会有更好、更完整的解决方案。

【讨论】:

  • 我没有使用 windows,但我使用 doMC 进行并行计算。我的函数 get.rho() 很糟糕,当然可以改进。我会将其包含在主要问题中。
  • -1 为 7200 万行数据集建议 plyr。特别是说 plyr 是为此量身定做的?
  • @MatthewDowle 很公平。我打算将其作为一种可能性练习,而不是一个完美的答案。但是,你是对的,plyr 中的 7200 万行是一个可怕的想法。
猜你喜欢
  • 1970-01-01
  • 2016-09-23
  • 2013-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
相关资源
最近更新 更多