【问题标题】:How to apply a complex function across crossed levels of factors in data.frame (in R)?如何在data.frame(在R中)中跨跨级别的因素应用一个复杂的函数?
【发布时间】:2015-07-25 13:35:14
【问题描述】:

我想在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 应用于fact1 和fact2 的每个交叉级别(这里是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

如何解决这个编程问题?

【问题讨论】:

    标签: r aggregate apply


    【解决方案1】:

    您可以使用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 的种子

    【讨论】:

    • 好的,这行得通。有没有办法在不转换为 data.table 的情况下做到这一点?
    • @tomka 我也发了dplyr的版本,不需要转换成data.table
    【解决方案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 
    

    数据框参数上的拆分函数返回由基于交叉因子参数的子集组成的较小数据框。如果您需要将其作为向量,则可以用 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 
    

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-10
      • 1970-01-01
      • 1970-01-01
      • 2021-07-13
      • 1970-01-01
      • 1970-01-01
      • 2017-09-02
      • 1970-01-01
      相关资源
      最近更新 更多