sheet - ¿Se resume dplyr cuando el retorno de la función es vectorial?
select in r (2)
Es por eso que todavía amo a 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
La función dplyr::summarize()
puede aplicar funciones arbitrarias sobre los datos, pero parece que la función debe devolver un valor escalar. Tengo curiosidad por saber si hay una manera razonable de manejar las funciones que devuelven un valor vectorial sin hacer varias llamadas a la función.
Aquí hay un ejemplo mínimo un tanto tonto. Considere una función que da múltiples valores, tales como:
f <- function(x,y){
coef(lm(x ~ y, data.frame(x=x,y=y)))
}
y datos que se parecen a:
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))
Me gustaría hacer algo como:
df %>%
group_by(group) %>%
summarise(f(x,y))
y recupere una tabla que tiene 2 columnas agregadas para cada uno de los valores devueltos en lugar de la columna 1 habitual. En su lugar, este error con: Expecting single value
Por supuesto, podemos obtener múltiples valores de dlpyr::summarise()
dando el argumento de la función varias veces:
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))
Esto da la salida deseada:
group a b
1 A 1.7957245 -0.339992915
2 B 0.5283379 -0.004325209
3 C 1.0797647 -0.074393457
pero la codificación de esta manera es ridículamente cruda y fea.
data.table
maneja este caso más sucintamente:
dt <- as.data.table(df)
dt[, f(x,y), by="group"]
pero crea una salida que extiende la tabla usando filas adicionales en lugar de columnas adicionales, lo que resulta en una salida que es confusa y más difícil de trabajar con:
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
Por supuesto, hay estrategias de apply
más clásicas que podríamos usar aquí,
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
pero esto sacrifica tanto la elegancia como la sospecha de la velocidad de la agrupación. En particular, tenga en cuenta que no podemos usar nuestra función predefinida f
en este caso, pero tenemos que codificar la agrupación en la definición de la función.
¿Existe una función dplyr
para manejar este caso? Si no es así, ¿existe una manera más elegante de manejar este proceso de evaluación de funciones de valores vectoriales sobre un cuadro de datos por grupo?
Podrías intentar 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
La salida basada en f1
y f2
es
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
Actualizar
Si está utilizando data.table
, la opción para obtener un resultado similar es
library(data.table)
setnames(setDT(df)[, as.list(f(x,y)) , group], 2:3, c(''a'', ''b''))[]