【问题标题】:Divide et impera on a data frame in R在R中的数据帧上划分和帝国
【发布时间】:2012-05-14 04:47:58
【问题描述】:

众所周知,R 并不是运行大型分析的最有效平台。 如果我有一个包含三个参数的大型数据框:

GROUP   X  Y
A       1  2
A       2  2
A       2  3
...
B       1  1
B       2  3
B       1  4
...
millions of rows

我想在每个组上运行一个计算(例如在 X,Y 上计算 Pearson 的 r)并将结果存储在一个新的数据框中,我可以这样做:

df = loadDataFrameFrom( someFile )
results = data.frame()
for ( g in unique( df$GROUP)) ){
    gdf <- subset( df, df$GROUP == g )
    partialRes <- slowStuff( gdf$X,gdf$Y )
    results = rbind( results, data.frame( GROUP = g, RES = partialRes ) )
}
// results contains all the results here.
useResults(results)

明显的问题是这非常慢,即使在强大的多核机器上也是如此。

我的问题是:是否可以并行化这种计算,例如为每个组或一组组设置一个单独的线程? 是否有一个干净的 R 模式来解决这个简单的分而治之问题?

谢谢, 木龙

【问题讨论】:

  • 如何并行化很大程度上取决于计算类型,不是吗?
  • 您是否刻意尝试尽一切可能使此过程尽可能慢?我不确定你写这个效率会不会更低。
  • +1 乔希的评论。 rbind 在循环中经常出现,也许 R 本身可以检测到它并发出警告。警告消息可能是“在 for() 循环的最后一行检测到 rbind,这可能非常慢。请参阅 XYZ 参考以获取建议。”。也许需要解析器中的一个 if() 语句?
  • @MatthewDowle 如果实现了,我会坚持伴随一个 Clippy 的小弹出窗口说“看起来你正在尝试在一个 for 循环..." :)
  • 也许Revolution会认为这是一个更显着的特点?

标签: r parallel-processing dataframe divide-and-conquer


【解决方案1】:

首先,R 不一定慢。它的速度很大程度上取决于正确使用它,就像任何语言一样。有几件事可以在不做太多改动的情况下加速你的代码:在你开始之前预先分配你的resultsdata.frame;使用列表和矩阵或向量构造而不是 data.frame;切换到使用data.table;名单还在继续,但The R Inferno 是一个很好的起点。

另外,看看here。它很好地总结了如何利用多核机器。

Hadley Wickam 用他的 plyr 包,特别是 ddply 简洁地解决了“干净的 R 模式”:

library(plyr)
library(doMC)
registerDoMC()
ddply(df, .(GROUP), your.function, .parallel=TRUE)

但是,它不一定很快。你可以使用类似的东西:

library(parallel)
mclapply(unique(df$GRUOP), function(x, df)  ...)

或者最后,您可以使用foreach 包:

foreach(g = unique(df$Group), ...) %dopar$ {
   your.analysis
}

【讨论】:

  • R Inferno 看起来很棒,非常感谢您的提示!很高兴为 Java/C 程序员编写一个 R 教程,展示主要区别......
【解决方案2】:

支持我的评论:1000 万行,26 个组。在单核 3.3Ghz CPU 上在

> set.seed(21)
> x <- data.frame(GROUP=sample(LETTERS,1e7,TRUE),X=runif(1e7),Y=runif(1e7))
> system.time( y <- do.call(rbind, lapply(split(x,x$GROUP),
+     function(d) data.frame(GROUP=d$GROUP[1],cor=cor(d$X,d$Y)))) )
   user  system elapsed 
   2.37    0.56    2.94 
> y
  GROUP           cor
A     A  2.311493e-03
B     B -1.020239e-03
C     C -1.735044e-03
D     D  1.355110e-03
E     E -8.027199e-04
F     F  8.234086e-04
G     G  2.337217e-04
H     H -5.861781e-04
I     I  7.799191e-04
J     J  1.063772e-04
K     K  7.174137e-04
L     L  4.151059e-04
M     M  4.440694e-04
N     N  2.568411e-03
O     O -3.827366e-04
P     P -1.239380e-03
Q     Q -1.057020e-03
R     R  1.079676e-03
S     S -1.819232e-03
T     T -3.577533e-04
U     U -1.084114e-03
V     V  6.686503e-05
W     W -1.631912e-03
X     X  8.668508e-04
Y     Y -6.460281e-04
Z     Z  1.614978e-03

顺便说一句,只有当您的 slowStuff 函数是瓶颈时,并行化才会有所帮助。您在循环中使用rbind 可能是瓶颈,除非您在slowStuff 中执行类似操作。

【讨论】:

  • 我并没有真正关注 rbind 部分,这确实非常慢,而是在 slowStuff 部分。这就是我想并行计算的原因。我不打算采用更快的线性方法,而是采用并行方法。
  • @Mulone:我鼓励您在解决问题之前先分析您的代码。您可能会惊讶地发现,更快的线性方法仍然比并行方法更快。
【解决方案3】:

我认为您的缓慢部分是由于您在 R 中的非 R 编程。以下将为您提供每组的相关性(我使用 mtcars 数据集并将其除以 cyl 组)并且做得非常快:

by(mtcars, mtcars$cyl, cor)

【讨论】:

    猜你喜欢
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-12
    • 2022-12-10
    • 1970-01-01
    • 2020-10-14
    • 1970-01-01
    相关资源
    最近更新 更多