如何在 data.frame(在 R 中)中跨交叉因子水平应用复杂函数?
How to apply a complex function across crossed levels of factors in data.frame (in R)?
我想在 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
我该如何解决这个编程问题?
您可以使用 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和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
数据帧参数上的拆分函数 returns 由基于交叉因子参数的子集组成的较小数据帧。如果您需要它作为向量,可以用 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])
}
我想在 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
我该如何解决这个编程问题?
您可以使用 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和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
数据帧参数上的拆分函数 returns 由基于交叉因子参数的子集组成的较小数据帧。如果您需要它作为向量,可以用 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])
}