私はdplyr構文に少し苦労しています。異なる変数と1つのグループ化変数を持つデータフレームがあります。それでは、Rのdplyrを使って、各グループ内の各列の平均を計算したいと思います。
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))
これにより、 "grp"で示される各グループの列 "a"の平均がわかります。
私の質問は、次のとおりです。各グループ内の各列の平均を一度に取得することは可能ですか。それとも、各列にdf %>% group_by(grp) %>% summarise(mean(a))
を繰り返す必要がありますか?
私が欲しいのはそのようなものです。
df %>% group_by(grp) %>% summarise(mean(a:d)) # "mean(a:d)" does not work
この目的のために、dplyr
パッケージにはsummarise_all
が含まれています。
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
特定の列だけを要約したい場合は、summarise_at
またはsummarise_if
関数を使用してください。
あるいは、purrrlyr
パッケージが同じ機能を提供します。
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
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
パフォーマンスを比較してみましょう。
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
summarize_at
にsummarize_all
、summarize_if
、およびdplyr 0.7.4
を使用して要約することができます。以下のコードのようにvars
およびfuns
引数を使用して、複数の列と関数を設定できます。 funs式の左辺は、要約されたvarsの接尾辞に割り当てられています。 dplyr 0.7.4
では、summarise_each
(およびmutate_each
)はすでに推奨されていないため、これらの関数は使用できません。
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
次のコードに変更する必要があります。以下のコードはすべて同じ結果になります。
# 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
複数の機能を持つこともできます。
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
summarise
にもっと多くの引数を渡すことができます。
df %>% group_by(grp) %>% summarise(mean(a), mean(b), mean(c), mean(d))
ソース:ローカルデータフレーム[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
完全を期すために:dplyr v0.2の場合ddply
とcolwise
の組み合わせでも、次のようになります。
> 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
しかし、少なくともこの場合は遅くなります。
> 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
すべての例は素晴らしいですが、「きちんとした」フォーマットで作業すると作業がいかに簡単になるかを示すために、もう1つ追加します。今のところデータフレームは "ワイド"フォーマットで、変数 "a"から "d"は列で表現されています。 "整然とした"(または長い)形式にするには、 "a"から "d"までの列の変数を行にシフトするtidyr
パッケージのgather()
を使用できます。次に、group_by()
関数とsummarize()
関数を使って各グループの平均を求めます。データをワイドフォーマットで表示したい場合は、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