Menu

R のグループ化と集計(aggregate, tapply, dplyr)

R でグループごとの統計量を計算するあらゆる方法: table() による集計、グループごとに 1 つの統計量を返す tapply()、aggregate() の式インターフェース、そして dplyr の group_by() と summarize()。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

グループごとの行数を数える: table()

最も単純なグループ化の問いは「それぞれいくつあるか?」であり、base R は 1 回の呼び出しで答えます。table() は各値が現れる回数を数えます。

列が 1 つなら値ごとの件数を返し、2 つなら完全なクロス集計になります(地域が行、プランが列)。「割合は?」を知りたいときは prop.table(table(...)) で包みます。table() は R 全体で度数の答えに至る最速の経路です — 件数だけが必要なときに、もっと重いものに手を伸ばさないでください。

グループごとに 1 つの統計量: tapply()

tapply(values, groups, function) は第 1 のベクトルを第 2 のベクトルで分割し、各断片に関数を適用します。

結果は 名前付きベクトル です — グループのラベルが名前、統計量が値 — なので、素早い参照(means["EU"])に最適です。名前は "table apply" と読み解けます。グループ化の表に沿って関数を適用するのです。これは sapply()lapply() と同じファミリーに属し、apply ファミリーのドキュメント で扱っています。

tapply() の限界は形です。名前付きベクトルは結合したり、作図したり、処理を続けたりするには扱いにくいのです。要約が最終的な答えではなく 踏み石 であるときは、データフレームを返してほしくなります — それがまさに次の道具の返すものです。

式を使う主力: aggregate()

aggregate() は base R の本格的なグループ化です。データフレームを返し、その式インターフェースは口に出して言う文のように読めます。value ~ group は「value を group で分解する」という意味です。

グループ化列を増やすのは式の中で + another_column を書くだけです — 2 番目の呼び出しは region quarter の組み合わせごとに合計します。cbind(a, b) ~ group で複数の値の列を一度に集計することもできます。出力が普通のデータフレームなので、そのまま結合、並べ替え、作図に流し込めます — グループ集計の既定として選ぶべき base の道具です。

知っておきたい静かな挙動が 1 つあります。式インターフェースは、集計の前に、使用する列のどれかに NA がある行を落とします。たいていは望ましい動作ですが、ときどき件数が少なく見える理由になります。

dplyr のやり方: group_by() + summarize()

現代の標準は dplyr の 2 動詞パターンです — group_by() がグループ化を宣言し、summarize() が各グループを 1 行にまとめ、名前を付けただけの統計量を計算します(静的な例です。サンドボックスは base R しか実行しません)。

library(dplyr)

sales |>
    group_by(region) |>
    summarize(
        n     = n(),
        total = sum(amount),
        avg   = mean(amount)
    )

ここは dplyr が base R を本当に上回るところです。1 回の読みやすい呼び出しでグループごとに複数の統計量を計算でき(aggregate() はそのために曲芸を要します)、しかも n() がおまけで付き、結果はそのまま次のパイプへ流れます。(summarise() は英国式綴りの同じ関数です。)

初心者を戸惑わせることが 1 つあります。グループ化列が複数ある場合、summarize() は最後の 1 つだけを剥がし、結果はグループ化されたまま残ります — そしてそれについてメッセージを表示します。.groups 引数で望むものを明示しましょう: summarize(avg = mean(amount), .groups = "drop") は素のグループ化されていないフレームを返し、これが既定の習慣として正しいものです。グループ化されたままのフレームが後続のコードに紛れ込むと、mutate() などが黙ってグループ単位で動作します — 分かりにくい結果の典型的な原因です。

どれを選ぶか

  • 件数table()。1 回の呼び出しに勝るものはありません。
  • 統計量 1 つ、素早く見たいtapply()。名前付きベクトルから答えを読み取ります。
  • データフレームが欲しい、base のみの環境 — 式を使う aggregate()
  • 統計量が複数、パイプラインの一部、あるいは野心的な処理group_by() |> summarize()

これらの間に誤答はありません — どれも同じ数値を計算します — が、必要な出力の形に道具を合わせると、後の変換の手間が省けます。平均、中央値、ばらつきそのものは 記述統計 で扱っています。

実践例: 気筒数別の mtcars

組み込みデータセットですべてをまとめてみましょう — 気筒数ごとに、車が何台あり、燃費と馬力の平均はいくらか。

4 気筒の車が 11 台で平均およそ 26.7 mpg、6 気筒が 7 台で約 19.7、8 気筒が 14 台で約 15.1 — そして馬力は逆方向に進みます。2 回の呼び出しで要約表がまるごと。グループ集計が存在するのは、まさにこの種の問いに答えるためです。

この記事のまとめ

  • 件数には table()、割合には prop.table()
  • tapply(values, groups, fn) は名前付きベクトルを返します — 素早く見たいとき、参照しやすいときに。
  • aggregate(value ~ group, data, FUN) はデータフレームを返します。+ でグループ化列を、cbind() で値の列を増やせます。
  • dplyr の group_by() |> summarize(n = n(), avg = mean(x), .groups = "drop") が複数統計量の要約における現代の標準です。
  • 出力の形で選びましょう。数値についてはどれも一致します。

次は キーを共有する表の結合 — merge() と dplyr の join ファミリーです。

よくある質問

R でグループごとの平均を計算するにはどうすればよいですか?

base R には 2 つの道具があります。tapply(df$value, df$group, mean) はグループ平均の名前付きベクトルを返し、aggregate(value ~ group, data = df, FUN = mean) は同じ結果をデータフレームとして返します。dplyr では df |> group_by(group) |> summarize(avg = mean(value)) です。

R でグループごとの出現回数を数えるにはどうすればよいですか?

table(df$group) は 1 回の呼び出しでグループの値ごとに行数を数えます。table(df$a, df$b) は 2 つの列をクロス集計します。dplyr では count(df, group) が同じことを行い、データフレームを返すので処理を続けやすくなります。

R の aggregate() は何をしますか?

aggregate() は 1 つ以上のグループ化列でデータフレームを分割し、各断片に関数を適用し、結果をデータフレームとして返します。式インターフェースは自然に読めます: aggregate(sales ~ region + quarter, data = df, FUN = mean) は「sales を region と quarter でグループ化して平均する」という意味です。

tapply と aggregate の違いは何ですか?

計算は同じで、出力の形が異なります。tapply() は名前付きベクトル(グループ化列が 2 つなら配列)を返し、素早い参照に便利です。aggregate() はデータフレームを返し、結果をさらに分析・結合・作図に回すときに有利です。迷ったら aggregate() を使いましょう。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める