对于每个因子水平,在除当前水平之外的所有水平上聚合值。

3
对于每个因子水平,我需要提取除当前数据框之外的所有子集聚合值。例如,有几个学科在多天内完成反应时间任务,我需要计算所有学科和所有天数的平均反应时间,但不包括计算平均值的学科。目前,我这样做:
 library(lme4)
 ddply(sleepstudy, .(Subject, Days), summarise, 
       avg_rt = mean(sleepstudy[sleepstudy$Subject != Subject &
                   sleepstudy$Days == Days,"Reaction"]), .progress="text")

对于小数据集,它可以正常工作,但对于大数据集,速度可能非常慢。有没有更快的方法?

2个回答

3
#create big dataset
n <- 1e4
set.seed(1)
sleepstudy <- data.frame(Reaction=rnorm(n),Subject=1:4,Days=sort(rep((1:(n/4)),4)))


library(plyr)
system.time(
  res <- ddply(sleepstudy, .(Subject, Days), summarise, 
               avg_rt = mean(sleepstudy[sleepstudy$Subject != Subject &
                 sleepstudy$Days == Days,"Reaction"]))
)
#User      System      elapsed 
#6.532       0.013       6.556  

#use data.table for big datasets
library(data.table)

dt<- as.data.table(sleepstudy)
system.time(
 {dt[,avg_rt:=mean(Reaction),by=Days];
  dt[,n:=.N,by=Days];
  dt[,avg_rt:=(avg_rt*n-Reaction)/(n-1)]}
)
#User      System      elapsed 
#0.005       0.001       0.005 


#test if results are equal
dt2 <- as.data.table(res)
setkey(dt2,Subject,Days)
setkey(dt,Subject,Days)
all.equal(dt[,avg_rt],dt2[,avg_rt])
#[1] TRUE

对于非常大的数据集,速度提升应该更为明显。我只是不能与更大的数据集进行比较,因为ddply非常慢。

谢谢,它工作得很好。即使使用相同的算法ddply,data.table仍然更快。还可以使用以下语句控制每个Subject X Days组合的多个观测值:dt[,sn:=.N,by=c("Subject","Days")]; dt[,s_avg_rt:=ifelse(sn==1,Reaction,.SD[,sum(Reaction)]),by=c("Subject","Days")]; dt[,avg_rt1:=(avg_rt*n-s_avg_rt)/(n-sn)]; - Andrey Chetverikov
@Andrey Chetverikov 我做了一个小改动,大大提高了性能。 - Roland
@Roland 在 j 中可以进行自连接,例如:DT[,DT[.BY,...],by=...]。作用域可能会有些棘手(在哪个部分内的 by 列引用哪个 DT?),因此 .BY 对于这一点非常有用。尚未实现不连接(DT[-J(...)]),但是长手动 DT[-DT[J(...),which=TRUE] 仍然比向量扫描 != 更好。所以,非常丑陋,但也许是 DT[,DT[-DT[.BY,which=TRUE]],by=...]。然而,首先进行聚合步骤通常比从每个组进行连接要快得多。 - Matt Dowle
1
@Roland 而且 .I 也可能对此有用(一旦实现),例如 DT[,DT[-.I],by=...] - Matt Dowle
@Roland 是的,我也想要 .I。不过首先得处理好这19个未解决的bug报告 - Matt Dowle
显示剩余2条评论

0
也许使用 lapplyaggregate 会更快:
do.call("rbind", (lapply(unique(sleepstudy$Subject),
                         function(x)
                           cbind(Subject = x,
                                 aggregate(Reaction ~ Days,
                                           subset(sleepstudy, Subject != x),
                                           mean)))))

更新:

我使用了system.time比较了这两个命令,结果表明原始命令更慢。

library(lme4)
library(plyr)

system.time(
ddply(sleepstudy, .(Subject, Days), summarise, 
      avg_rt = mean(sleepstudy[sleepstudy$Subject != Subject &
                    sleepstudy$Days == Days,"Reaction"]))
)

   # user  system elapsed 
   # 0.17    0.00    0.22 

system.time(
do.call("rbind", (lapply(unique(sleepstudy$Subject),
                         function(x) 
                           cbind(Subject = x,
                                 aggregate(Reaction ~ Days,
                                           subset(sleepstudy, Subject != x),
                                           mean)))))
)


   # user  system elapsed 
   # 0.12    0.00    0.12 

对于小数据集,这比原始方法更有效,但对于大数据集,原始方法仍然更好。http://pastebin.com/Zb4CaJrN对于184320行数据,原始方法需要6.041秒,而lapply和aggregate需要10.96秒。 - Andrey Chetverikov

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