tidyverse - verbs dplyr




Resumindo várias colunas com dplyr? (4)

Eu estou lutando um pouco com a sintaxe dplyr. Eu tenho um quadro de dados com diferentes variáveis ​​e uma variável de agrupamento. Agora eu quero calcular a média para cada coluna dentro de cada grupo, usando dplyr em 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))

Isso me dá a média para a coluna "a" para cada grupo indicado por "grp".

Minha pergunta é: é possível obter os meios para cada coluna dentro de cada grupo de uma só vez? Ou eu tenho que repetir df %>% group_by(grp) %>% summarise(mean(a)) para cada coluna?

O que eu gostaria de ter é algo como

df %>% group_by(grp) %>% summarise(mean(a:d)) # "mean(a:d)" does not work

O pacote dplyr contém dplyr 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

Se você quiser resumir apenas algumas colunas, use as funções summarise_if ou summarise_if .

Alternativamente, o pacote purrrlyr fornece a mesma funcionalidade:

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

Também não se esqueça sobre 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

Vamos tentar comparar o desempenho.

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

Para completar: com dplyr v0.2, ddply com colwise também fará isso:

> 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

mas é mais lento, pelo menos neste 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

Todos os exemplos são ótimos, mas acho que adicionaria mais um para mostrar como o trabalho em um formato "arrumado" simplifica as coisas. No momento, o quadro de dados está no formato "amplo", o que significa que as variáveis ​​"a" a "d" são representadas em colunas. Para obter um formato "arrumado" (ou longo), você pode usar o tidyr gather() do pacote tidyr , que desloca as variáveis ​​nas colunas "a" a "d" em linhas. Então você usa as group_by() e summarize() para obter a média de cada grupo. Se você quiser apresentar os dados em um formato amplo, apenas faça uma chamada adicional para a função 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

Você pode simplesmente passar mais argumentos para summarise :

df %>% group_by(grp) %>% summarise(mean(a), mean(b), mean(c), mean(d))

Fonte: frame de dados 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




aggregate