如何在data.frame(在R中)中跨越多个因素应用复杂函数?

tom*_*mka 0 aggregate r apply

我想在一个data.frame与所做的相似的交叉因子水平上应用一个函数aggregate,但是对于比aggregate可以处理的更复杂的函数.

例如.

fact1=c(rep('A',6),rep('B',6))
fact2=c(rep(c(rep('C',3),rep('D',3)),2))
crit1=rnorm(12)
crit2=crit1+rnorm(12)
dat=data.frame(fact1,fact2,crit1,crit2)

target.fit = function(dat){
  mod=lm(dat$crit2~dat$crit1)
  return(mod$coefficients[2])
}
Run Code Online (Sandbox Code Playgroud)

此代码生成一个data.frame dat.目标是适用target.fit于每个交叉级别fact1fact2(此处为lm).

对于仅需要一个输入向量的函数(例如均值使用),这很简单aggregate.

> aggregate(dat,list(fact1=fact1,fact2=fact2),mean)
  fact1 fact2 fact1 fact2      crit1      crit2
1     A     C    NA    NA -0.5875951 -0.6048572
2     B     C    NA    NA  0.3712372  0.9135742
3     A     D    NA    NA -1.0163750 -2.4971846
4     B     D    NA    NA  0.3937682  0.6227697
Run Code Online (Sandbox Code Playgroud)

但是,aggregate对于多变量输入不起作用.

> aggregate(dat,list(fact1=fact1,fact2=fact2),target.fit)
 Error in dat$crit2 : $ operator is invalid for atomic vectors
Run Code Online (Sandbox Code Playgroud)

我该如何解决这个编程问题?

akr*_*run 5

您可以使用该formula方法来避免获取NA

 aggregate(.~fact1+fact2, dat, FUN=mean)
Run Code Online (Sandbox Code Playgroud)

对于自定义功能

 library(data.table)#v1.9.5+
 setDT(dat)[,target.fit(.SD) ,.(fact1, fact2)]
 #   fact1 fact2       V1
 #1:     A     C 1.060835
 #2:     A     D 1.259871
 #3:     B     C 1.451595
 #4:     B     D 1.766432
Run Code Online (Sandbox Code Playgroud)

这是一样的

 setDT(dat)[, coef(lm(crit2~crit1))[2] ,.(fact1, fact2)]
 #   fact1 fact2       V1
 #1:     A     C 1.060835
 #2:     A     D 1.259871
 #3:     B     C 1.451595
 #4:     B     D 1.766432
Run Code Online (Sandbox Code Playgroud)

或使用 dplyr

 library(dplyr)
 dat %>% 
     group_by(fact1, fact2) %>% 
     do(data.frame(V1=target.fit(.)))
 #  fact1 fact2       V1
 #1     A     C 1.060835
 #2     A     D 1.259871
 #3     B     C 1.451595
 #4     B     D 1.766432
Run Code Online (Sandbox Code Playgroud)

一个base R选项是

 sapply(split(dat, as.list(dat[paste0('fact',1:2)]), drop=FALSE), target.fit)
 #A.C.dat$crit1 B.C.dat$crit1 A.D.dat$crit1 B.D.dat$crit1 
 #   1.060835      1.451595      1.259871      1.766432 
Run Code Online (Sandbox Code Playgroud)

要么

  by(dat, list(dat$fact1, dat$fact2), FUN=target.fit)
Run Code Online (Sandbox Code Playgroud)

要获取data.frame中的因子级别,

  do.call(rbind,by(dat, list(dat$fact1, dat$fact2), 
           FUN=function(x) cbind(x[1,1:2], V1=target.fit(x))))
Run Code Online (Sandbox Code Playgroud)

注意:用作set.seed(24)创建的种子dat

  • @tomka我还发布了`dplyr`版本,它不需要转换为`data.table` (2认同)