将`mclapply`结果放回data.frame

时间:2014-08-14 18:47:33

标签: r parallel-processing lapply

我有一个非常大的 data.frame我想要应用一个相当复杂的函数来计算一个新列。我想要并行完成。这类似于over on the r listserve发布的问题,但第一个答案是错误的,第二个答案是无益的。

由于parallel包,我得到了一切,除了如何将输出放回到数据框之外。这是一个显示我所得到的MWE:

library(parallel)

# Example Data
data <- data.frame(a = rnorm(200), b = rnorm(200),  
                   group = sample(letters, 200, replace = TRUE))

# Break into list
datagroup <- split(data, factor(data$group))

# execute on each element in parallel
options(mc.cores = detectCores())
output <- mclapply(datagroup, function(x) x$a*x$b)

output中的结果是数字向量列表。我需要将它们添加到我可以附加到data的列中。我一直在查看do.call(cbind, ...)的行,但我有两个名称相同的列表,而不是我加入的单个列表。 melt(output)为我提供了一个向量,但其行与data的顺序不同。

5 个答案:

答案 0 :(得分:4)

从评论转换为答案..

这似乎有效:

data <- 
  do.call(
    rbind, mclapply(
      split(data, data$group), 
       function(x){
         z <- x$a*x$b
         x <- as.data.frame(cbind(x, newcol = z))
         return(x)
         }))
rownames(data) <- seq_len(nrow(data))
head(data)
#           a          b group      newcol
#1 -0.6482428  1.8136254     a -1.17566963
#2  0.4397603  1.3859759     a  0.60949714
#3 -0.6426944  1.5086339     a -0.96959055
#4 -1.2913493 -2.3984527     a  3.09724030
#5  0.2260140  0.1107935     a  0.02504087
#6  2.1555370 -0.7858066     a -1.69383520

由于您使用的是“非常大”的data.frame(大致有多大?),您是否考虑过使用dplyrdata.table来做什么?对于大型数据集,使用其中之一可能比使用mclapply更好。相当于:

library(dplyr)
data %>%
  group_by(group) %>%
  mutate(newcol = a * b)

library(data.table) 
setDT(data)[, newcol := a*b, by=group]

答案 1 :(得分:1)

有点过时了,但这可能会有所帮助。

如果你有很多分裂,rbind会在性能方面杀死你。

使用unsplit功能要快得多。

results <- mclapply( split(data, data$group), function(x) x$a*x$b) 

resultscombined <- unsplit (results, data$group)

data$newcol <-  resultscombined 

是的,内存命中,取决于你喜欢什么。

答案 2 :(得分:0)

我目前无法将parallel软件包下载到我的电脑上。在这里,我发布了一个适用于我常用设置的解决方案,使用snow包进行并行计算。

解决方案只是在开始时对data.frame进行排序,然后合并调用c()的输出列表。见下文:

library(snow)
library(rlecuyer)

# Example data
data <- data.frame(a = rnorm(200), b = rnorm(200),  
                   group = sample(letters, 200, replace = TRUE))
data <- data[order(data$group),]

# Cluster setup
clNode <- list(host="localhost")
localCl <- makeSOCKcluster(rep(clNode, 2))
clusterSetupRNG(localCl, type="RNGstream", seed=sample(0:9,6,replace=TRUE))
clusterExport(localCl, list=ls())

# Break into list
datagroup <- split(data, factor(data$group))

output <- clusterApply(localCl, datagroup, function(x){ x$a*x$b })

# Put back and check
data$output <- do.call(c, output)
data$check <- data$a*data$b

all(data$output==data$check)

# Stop cluster
stopCluster(localCl)

答案 3 :(得分:0)

受@beginneR和我们对dplyr的共同爱好的启发,我做了一些更多的摆弄,并认为实现这一目标的最佳方法是

 rbind_all( mclapply(split(data, data$group), fun(x) as.data.frame(x$a*x$b)))

答案 4 :(得分:0)

使用多核过程按组计算平均值:

library(dplyr)
x <- group_by(iris, Species)
indices <- attr(x,"indices")
labels <- attr(x,"labels") 

require(parallel)
result <- mclapply(indices,  function(indx){
                   data <- slice(iris, indx + 1)
                   ## Do something...
                   mean(data$Petal.Length)
                   }, mc.cores =2)

 out <- cbind(labels,mean=unlist(result))
 out
 ##      Species  mean
 ## 1     setosa 1.462
 ## 2 versicolor 4.260
 ## 3  virginica 5.552