studio - filtros en r
¿Resumiendo múltiples columnas con dplyr? (5)
El paquete dplyr
contiene summarise_all
para este objetivo:
df %>% group_by(grp) %>% summarise_all(funs(mean))
#> Source: local data frame [3 x 5]
#>
#> grp a b c d
#> (int) (dbl) (dbl) (dbl) (dbl)
#> 1 1 3.000000 2.666667 2.666667 3.333333
#> 2 2 2.666667 2.666667 2.500000 2.833333
#> 3 3 4.000000 1.000000 4.000000 3.000000
Si desea resumir solo ciertas columnas, use las funciones summarise_at
o summarise_if
.
Alternativamente, el paquete purrrlyr
proporciona la misma funcionalidad:
df %>% slice_rows("grp") %>% dmap(mean)
#> Source: local data frame [3 x 5]
#>
#> grp a b c d
#> (int) (dbl) (dbl) (dbl) (dbl)
#> 1 1 3.000000 2.666667 2.666667 3.333333
#> 2 2 2.666667 2.666667 2.500000 2.833333
#> 3 3 4.000000 1.000000 4.000000 3.000000
Además, no te olvides de data.table
:
setDT(df)[, lapply(.SD, mean), by = grp]
#> grp a b c d
#> 1: 3 3.714286 3.714286 2.428571 2.428571
#> 2: 1 1.000000 4.000000 5.000000 2.000000
#> 3: 2 4.000000 4.500000 3.000000 3.000000
Tratemos de comparar el rendimiento.
library(dplyr)
library(purrrlyr)
library(data.table)
library(benchr)
n <- 10000
df <- data.frame(
a = sample(1:5, n, replace = TRUE),
b = sample(1:5, n, replace = TRUE),
c = sample(1:5, n, replace = TRUE),
d = sample(1:5, n, replace = TRUE),
grp = sample(1:3, n, replace = TRUE)
)
dt <- setDT(df)
benchmark(
dplyr = df %>% group_by(grp) %>% summarise_all(funs(mean)),
purrrlyr = df %>% slice_rows("grp") %>% dmap(mean),
data.table = dt[, lapply(.SD, mean), by = grp]
)
#> Benchmark summary:
#> Time units : microseconds
#> expr n.eval min lw.qu median mean up.qu max total relative
#> dplyr 100 3490 3550 3710 3890 3780 15100 389000 6.98
#> purrrlyr 100 2540 2590 2680 2920 2860 12000 292000 5.04
#> data.table 100 459 500 531 563 571 1380 56300 1.00
Esta pregunta ya tiene una respuesta aquí:
Estoy luchando un poco con la sintaxis dplyr. Tengo un marco de datos con diferentes variables y una variable de agrupación. Ahora quiero calcular la media de cada columna dentro de cada grupo, usando dplyr en R.
df <- data.frame(
a = sample(1:5, n, replace = TRUE),
b = sample(1:5, n, replace = TRUE),
c = sample(1:5, n, replace = TRUE),
d = sample(1:5, n, replace = TRUE),
grp = sample(1:3, n, replace = TRUE)
)
df %>% group_by(grp) %>% summarise(mean(a))
Esto me da el promedio para la columna "a" para cada grupo indicado por "grp".
Mi pregunta es: ¿es posible obtener los medios para cada columna dentro de cada grupo a la vez? ¿O tengo que repetir df %>% group_by(grp) %>% summarise(mean(a))
para cada columna?
Lo que me gustaría tener es algo así como
df %>% group_by(grp) %>% summarise(mean(a:d)) # "mean(a:d)" does not work
Para completar: con dplyr v0.2 ddply
con colwise
también hará esto:
> ddply(df, .(grp), colwise(mean))
grp a b c d
1 1 4.333333 4.00 1.000000 2.000000
2 2 2.000000 2.75 2.750000 2.750000
3 3 3.000000 4.00 4.333333 3.666667
pero es más lento, al menos en este caso:
> microbenchmark(ddply(df, .(grp), colwise(mean)),
df %>% group_by(grp) %>% summarise_each(funs(mean)))
Unit: milliseconds
expr min lq mean
ddply(df, .(grp), colwise(mean)) 3.278002 3.331744 3.533835
df %>% group_by(grp) %>% summarise_each(funs(mean)) 1.001789 1.031528 1.109337
median uq max neval
3.353633 3.378089 7.592209 100
1.121954 1.133428 2.292216 100
Podemos resumir usando summarize_at
, summarize_all
y summarize_if
en dplyr 0.7.4
. Podemos establecer las múltiples columnas y funciones usando vars
y funs
argument como el código a continuación. El lado izquierdo de la fórmula funs se asigna al sufijo de vars resumidos. En dplyr 0.7.4
, summarise_each
(y mutate_each
) ya está en desuso, por lo que no podemos usar estas funciones.
options(scipen = 100, dplyr.width = Inf, dplyr.print_max = Inf)
library(dplyr)
packageVersion("dplyr")
# [1] ‘0.7.4’
set.seed(123)
df <- data_frame(
a = sample(1:5, 10, replace=T),
b = sample(1:5, 10, replace=T),
c = sample(1:5, 10, replace=T),
d = sample(1:5, 10, replace=T),
grp = as.character(sample(1:3, 10, replace=T)) # For convenience, specify character type
)
df %>% group_by(grp) %>%
summarise_each(.vars = letters[1:4],
.funs = c(mean="mean"))
# `summarise_each()` is deprecated.
# Use `summarise_all()`, `summarise_at()` or `summarise_if()` instead.
# To map `funs` over a selection of variables, use `summarise_at()`
# Error: Strings must match column names. Unknown columns: mean
Deberías cambiar al siguiente código. Los siguientes códigos tienen el mismo resultado.
# summarise_at
df %>% group_by(grp) %>%
summarise_at(.vars = letters[1:4],
.funs = c(mean="mean"))
df %>% group_by(grp) %>%
summarise_at(.vars = names(.)[1:4],
.funs = c(mean="mean"))
df %>% group_by(grp) %>%
summarise_at(.vars = vars(a,b,c,d),
.funs = c(mean="mean"))
# summarise_all
df %>% group_by(grp) %>%
summarise_all(.funs = c(mean="mean"))
# summarise_if
df %>% group_by(grp) %>%
summarise_if(.predicate = function(x) is.numeric(x),
.funs = funs(mean="mean"))
# A tibble: 3 x 5
# grp a_mean b_mean c_mean d_mean
# <chr> <dbl> <dbl> <dbl> <dbl>
# 1 1 2.80 3.00 3.6 3.00
# 2 2 4.25 2.75 4.0 3.75
# 3 3 3.00 5.00 1.0 2.00
También puedes tener múltiples funciones.
df %>% group_by(grp) %>%
summarise_at(.vars = letters[1:2],
.funs = c(Mean="mean", Sd="sd"))
# A tibble: 3 x 5
# grp a_Mean b_Mean a_Sd b_Sd
# <chr> <dbl> <dbl> <dbl> <dbl>
# 1 1 2.80 3.00 1.4832397 1.870829
# 2 2 4.25 2.75 0.9574271 1.258306
# 3 3 3.00 5.00 NA NA
Simplemente puede pasar más argumentos para summarise
:
df %>% group_by(grp) %>% summarise(mean(a), mean(b), mean(c), mean(d))
Fuente: marco de datos local [3 x 5]
grp mean(a) mean(b) mean(c) mean(d)
1 1 2.500000 3.500000 2.000000 3.0
2 2 3.800000 3.200000 3.200000 2.8
3 3 3.666667 3.333333 2.333333 3.0
Todos los ejemplos son geniales, pero creo que agregaría uno más para mostrar cómo trabajar en un formato "ordenado" simplifica las cosas. En este momento, el marco de datos está en formato "ancho", lo que significa que las variables "a" a "d" están representadas en columnas. Para llegar a un formato "ordenado" (o largo), puede usar gather()
del paquete tidyr
que cambia las variables de las columnas "a" a "d" en filas. Luego usa las group_by()
y summarize()
para obtener la media de cada grupo. Si desea presentar los datos en un formato amplio, simplemente agregue una llamada adicional a la función spread()
.
library(tidyverse)
# Create reproducible df
set.seed(101)
df <- tibble(a = sample(1:5, 10, replace=T),
b = sample(1:5, 10, replace=T),
c = sample(1:5, 10, replace=T),
d = sample(1:5, 10, replace=T),
grp = sample(1:3, 10, replace=T))
# Convert to tidy format using gather
df %>%
gather(key = variable, value = value, a:d) %>%
group_by(grp, variable) %>%
summarize(mean = mean(value)) %>%
spread(variable, mean)
#> Source: local data frame [3 x 5]
#> Groups: grp [3]
#>
#> grp a b c d
#> * <int> <dbl> <dbl> <dbl> <dbl>
#> 1 1 3.000000 3.5 3.250000 3.250000
#> 2 2 1.666667 4.0 4.666667 2.666667
#> 3 3 3.333333 3.0 2.333333 2.333333