グループごとの行数を数える: 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() を使いましょう。