如何在data.frame(R中)的跨层因子中应用复杂函数?

时间:2015-07-25 13:35:14

标签: r aggregate 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])
}

此代码会生成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

但是,aggregate不适用于多变量输入。

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

如何解决此编程问题?

2 个答案:

答案 0 :(得分:5)

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

 aggregate(.~fact1+fact2, dat, FUN=mean)

对于自定义功能

 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

相同
 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

或使用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

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 

或者

  by(dat, list(dat$fact1, dat$fact2), FUN=target.fit)

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

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

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

答案 1 :(得分:2)

在data.table和dplyr之前的日子里,标准方法是lapply(split(data,fators),func)

> lapply( split( dat, list(fact1, fact2) ), target.fit)
$A.C
dat$crit1 
 1.328941 

$B.C
dat$crit1 
0.3281161 

$A.D
dat$crit1 
 -0.10337 

$B.D
dat$crit1 
   2.8962 

dataframe参数上的split函数返回基于交叉因子参数组成的子集的较小数据帧。如果你需要它作为一个向量,sapply函数可以代替lapply:

> sapply( split( dat, list(fact1, fact2) ), target.fit)
A.C.dat$crit1 B.C.dat$crit1 A.D.dat$crit1 B.D.dat$crit1 
    1.3289409     0.3281161    -0.1033700     2.8962000 

我可能会编写函数来传递数据参数lm的dat参数:

target.fit = function(dat){
  mod=lm(crit2~$crit1, data=dat)
  return(mod$coefficients[2])
}