Je me bats un peu avec la syntaxe dplyr. J'ai un bloc de données avec différentes variables et une variable de regroupement. Maintenant, je veux calculer la moyenne pour chaque colonne dans chaque groupe, en utilisant dplyr dans 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))
Cela me donne la moyenne pour la colonne "a" pour chaque groupe indiqué par "grp".
Ma question est la suivante: est-il possible d’obtenir les moyens pour chaque colonne à la fois dans chaque groupe? Ou dois-je répéter df %>% group_by(grp) %>% summarise(mean(a))
pour chaque colonne?
Ce que j'aimerais avoir, c'est quelque chose comme
df %>% group_by(grp) %>% summarise(mean(a:d)) # "mean(a:d)" does not work
Le package dplyr
contient summarise_all
pour cet objectif:
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 vous souhaitez résumer uniquement certaines colonnes, utilisez les fonctions summarise_at
ou summarise_if
.
Sinon, le package purrrlyr
fournit les mêmes fonctionnalités:
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
N'oubliez pas non plus 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
Essayons de comparer les performances.
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
Nous pouvons résumer en utilisant summarize_at
, summarize_all
et summarize_if
sur dplyr 0.7.4
. Nous pouvons définir les multiples colonnes et fonctions en utilisant les arguments vars
et funs
comme ci-dessous. La partie gauche de la formule de funs est affectée au suffixe des vars résumés. Dans le dplyr 0.7.4
, summarise_each
(et mutate_each
) est déjà obsolète, nous ne pouvons donc pas utiliser ces fonctions.
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
Vous devriez changer le code suivant. Les codes suivants ont tous le même résultat.
# 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
Vous pouvez également avoir plusieurs fonctions.
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
Vous pouvez simplement passer plus d'arguments à summarise
:
df %>% group_by(grp) %>% summarise(mean(a), mean(b), mean(c), mean(d))
Source: trame de données locale [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
Pour être complet: avec dplyr v0.2 ddply
avec colwise
fera également ceci:
> 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
mais c'est plus lent, du moins dans ce cas:
> 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
Tous les exemples sont excellents, mais je pense en ajouter un de plus pour montrer à quel point le travail dans un format "ordonné" simplifie les choses. Actuellement, le bloc de données est au format "large", ce qui signifie que les variables "a" à "d" sont représentées en colonnes. Pour obtenir un format "ordonné" (ou long), vous pouvez utiliser gather()
du package tidyr
qui décale les variables des colonnes "a" à "d" en lignes. Ensuite, vous utilisez les fonctions group_by()
et summarize()
pour obtenir la moyenne de chaque groupe. Si vous souhaitez présenter les données dans un format large, ajoutez simplement un appel supplémentaire à la fonction 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