dplyr 总结函数 return 何时为向量值?
dplyr summarise when function return is vector-valued?
dplyr::summarize()
函数可以对数据应用任意函数,但函数似乎必须 return 一个标量值。我很好奇是否有一种合理的方法来处理 return 向量值的函数,而无需多次调用该函数。
这是一个有点傻的最小示例。考虑一个给出多个值的函数,例如:
f <- function(x,y){
coef(lm(x ~ y, data.frame(x=x,y=y)))
}
数据看起来像:
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))
我想做类似的事情:
df %>%
group_by(group) %>%
summarise(f(x,y))
并返回一个 table,其中为每个 returned 值添加了 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))
这给出了所需的输出:
group a b
1 A 1.7957245 -0.339992915
2 B 0.5283379 -0.004325209
3 C 1.0797647 -0.074393457
但是以这种方式编码非常粗糙和丑陋。
data.table
更简洁地处理了这种情况:
dt <- as.data.table(df)
dt[, f(x,y), by="group"]
但是创建了一个使用额外的行而不是额外的列扩展 table 的输出,导致输出既混乱又难以处理:
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
当然还有更多经典的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
但这牺牲了优雅,我怀疑分组的速度。特别要注意的是,在这种情况下我们不能使用预定义函数 f
,而是必须将分组硬编码到函数定义中。
是否有dplyr
函数来处理这种情况?如果不是,是否有更优雅的方法来处理按组评估 data.frame 上的向量值函数的过程?
你可以试试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
基于f1
和f2
的输出是
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
更新
如果您使用 data.table
,获得类似结果的选项是
library(data.table)
setnames(setDT(df)[, as.list(f(x,y)) , group], 2:3, c('a', 'b'))[]
这就是为什么我仍然喜欢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
dplyr::summarize()
函数可以对数据应用任意函数,但函数似乎必须 return 一个标量值。我很好奇是否有一种合理的方法来处理 return 向量值的函数,而无需多次调用该函数。
这是一个有点傻的最小示例。考虑一个给出多个值的函数,例如:
f <- function(x,y){
coef(lm(x ~ y, data.frame(x=x,y=y)))
}
数据看起来像:
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))
我想做类似的事情:
df %>%
group_by(group) %>%
summarise(f(x,y))
并返回一个 table,其中为每个 returned 值添加了 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))
这给出了所需的输出:
group a b
1 A 1.7957245 -0.339992915
2 B 0.5283379 -0.004325209
3 C 1.0797647 -0.074393457
但是以这种方式编码非常粗糙和丑陋。
data.table
更简洁地处理了这种情况:
dt <- as.data.table(df)
dt[, f(x,y), by="group"]
但是创建了一个使用额外的行而不是额外的列扩展 table 的输出,导致输出既混乱又难以处理:
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
当然还有更多经典的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
但这牺牲了优雅,我怀疑分组的速度。特别要注意的是,在这种情况下我们不能使用预定义函数 f
,而是必须将分组硬编码到函数定义中。
是否有dplyr
函数来处理这种情况?如果不是,是否有更优雅的方法来处理按组评估 data.frame 上的向量值函数的过程?
你可以试试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
基于f1
和f2
的输出是
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
更新
如果您使用 data.table
,获得类似结果的选项是
library(data.table)
setnames(setDT(df)[, as.list(f(x,y)) , group], 2:3, c('a', 'b'))[]
这就是为什么我仍然喜欢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