dplyr总结函数返回是否为向量值?

cbo*_*tig 20 r dplyr

dplyr::summarize()函数可以对数据应用任意函数,但似乎函数必须返回标量值.我很好奇是否有合理的方法来处理返回向量值的函数而不需要多次调用该函数.

这是一个有点愚蠢的最小例子.考虑一个提供多个值的函数,例如:

f <- function(x,y){
  coef(lm(x ~ y, data.frame(x=x,y=y)))
}
Run Code Online (Sandbox Code Playgroud)

和看起来像这样的数据:

df <- data.frame(group=c('A','A','A','A','B','B','B','B','C','C','C','C'), x=rnorm(12,1,1), y=rnorm(12,1,1))
Run Code Online (Sandbox Code Playgroud)

我想做点什么:

df %>% 
group_by(group) %>%
summarise(f(x,y))
Run Code Online (Sandbox Code Playgroud)

并返回一个表,该表为每个返回值添加了2列,而不是通常的1列.相反,这个错误:Expecting single value

当然,我们可以通过多次dlpyr::summarise()给出函数参数来获取多个值:

f1 <- function(x,y) coef(lm(x ~ y, data.frame(x=x,y=y)))[[1]]
f2 <- function(x,y) coef(lm(x ~ y, data.frame(x=x,y=y)))[[2]]

df %>% 
group_by(group) %>%
summarise(a = f1(x,y), b = f2(x,y))
Run Code Online (Sandbox Code Playgroud)

这给出了所需的输出:

  group         a            b
1     A 1.7957245 -0.339992915
2     B 0.5283379 -0.004325209
3     C 1.0797647 -0.074393457
Run Code Online (Sandbox Code Playgroud)

但以这种方式编码是荒谬粗暴和丑陋的.

data.table 更简洁地处理这个案子:

dt <- as.data.table(df)
dt[, f(x,y), by="group"]
Run Code Online (Sandbox Code Playgroud)

但创建一个输出,使用额外的行而不是其他列来扩展表,从而导致输出既困惑又难以使用:

 group           V1
1:     A  1.795724536
2:     A -0.339992915
3:     B  0.528337890
4:     B -0.004325209
5:     C  1.079764710
6:     C -0.074393457
Run Code Online (Sandbox Code Playgroud)

当然,apply我们可以在这里使用更多经典策略,

sapply(levels(df$group), function(x) coef(lm(x~y, df[df$group == x, ])))


                     A            B           C
(Intercept)  1.7957245  0.528337890  1.07976471
y           -0.3399929 -0.004325209 -0.07439346
Run Code Online (Sandbox Code Playgroud)

但这牺牲了优雅,我怀疑分组的速度.特别要注意的是,f在这种情况下我们不能使用我们的预定义函数,但必须将分组硬编码到函数定义中.

dplyr处理这种情况的功能吗?如果没有,是否有一种更优雅的方法来处理这一过程,即按组分组评估向量值函数?

akr*_*run 16

你可以试试 do

library(dplyr)
 df %>%
    group_by(group) %>%
    do(setNames(data.frame(t(f(.$x, .$y))), letters[1:2]))
 # group         a           b
 #1     A 0.8983217 -0.04108092
 #2     B 0.8945354  0.44905220
 #3     C 1.2244023 -1.00715248
Run Code Online (Sandbox Code Playgroud)

输出基于f1f2

df %>% 
  group_by(group) %>%
  summarise(a = f1(x,y), b = f2(x,y))
#  group         a           b
#1     A 0.8983217 -0.04108092
#2     B 0.8945354  0.44905220
#3     C 1.2244023 -1.00715248
Run Code Online (Sandbox Code Playgroud)

更新

如果您正在使用data.table,获得类似结果的选项是

 library(data.table)
 setnames(setDT(df)[, as.list(f(x,y)) , group], 2:3, c('a', 'b'))[]
Run Code Online (Sandbox Code Playgroud)


jen*_*yan 7

这就是我仍然喜欢的原因plyr::ddply():

library(plyr)
f <- function(z) setNames(coef(lm(x ~ y, z)), c("a", "b"))
ddply(df, ~ group, f)
#   group           a          b
# 1     A   0.5213133 0.04624656
# 2     B   0.3020656 0.01450137
# 3     C   0.2189537 0.22998823
Run Code Online (Sandbox Code Playgroud)