← 授業トップへ戻る


本日の目標

  1. group_by() + summarise() でグループ別集計ができる
  2. count() でカテゴリの頻度を集計できる
  3. 集計結果を ggplot2 でグラフ化できる

準備:パッケージの読み込み

この回のコードは,すべて tidyverse を読み込んだ状態で動きます。 RStudioを起動したら,まずこれを実行してください。

library(tidyverse)

毎回必要です。読み込まないまま filter() や ggplot() を使うと, 「関数が見つかりません」というエラーになります。


1. summarise():集計

summarise() はデータ全体を1行(または数行)に集約します。

# msleep の全体の睡眠時間の平均
msleep |>
  summarise(mean_sleep = mean(sleep_total, na.rm = TRUE))
# 複数の統計量をまとめて計算
msleep |>
  summarise(
    n = n(),
    mean_sleep = mean(sleep_total, na.rm = TRUE),
    sd_sleep   = sd(sleep_total, na.rm = TRUE),
    min_sleep  = min(sleep_total, na.rm = TRUE),
    max_sleep  = max(sleep_total, na.rm = TRUE)
  )

よく使う集計関数

関数 意味
mean(x, na.rm=TRUE) 平均値
sd(x, na.rm=TRUE) 標準偏差
median(x, na.rm=TRUE) 中央値
min(x) / max(x) 最小値 / 最大値
n() データ件数
sum(x, na.rm=TRUE) 合計

na.rm = TRUE は欠損値(NA)を除いて計算するオプションです。


2. group_by() + summarise():グループ別集計

group_by() と summarise() を組み合わせると,グループごとに集計できます。

# 食性(vore)ごとの平均睡眠時間
msleep |>
  group_by(vore) |>
  summarise(
    n = n(),
    mean_sleep = mean(sleep_total, na.rm = TRUE),
    sd_sleep   = sd(sleep_total, na.rm = TRUE)
  )

出てきた <NA> の行は何か

出力の最後に <NA> という行があります。これは「NAという食性」ではありません。 msleep には食性が記録されていない動物が7種あり,その7種がひとまとめにされて集計されています。

group_by() は,欠損値も1つのグループとして扱います。黙って捨てたりはしません。 「そういうデータがある」と見せてくれている,と考えてください。

この行をどう扱うかは,分析する人が決めます。

  • 食性どうしを比べるのが目的なら,除く(4節でそうしています)
  • 「食性が不明な動物がこれだけいる」こと自体が情報なら,残す

どちらでも構いませんが,除いたことを自覚しておくのが大事です。 気づかないうちに7種が消えているのと,理由があって除くのとは違います。

# irisデータ:種ごとの平均と標準偏差
iris |>
  group_by(Species) |>
  summarise(
    n = n(),
    mean_sepal = mean(Sepal.Length),
    sd_sepal   = sd(Sepal.Length),
    mean_petal = mean(Petal.Length),
    sd_petal   = sd(Petal.Length)
  )

3. count():カテゴリの頻度集計

# 食性ごとの件数
msleep |>
  count(vore)
# 降順で並べる
msleep |>
  count(vore, sort = TRUE)

4. 集計結果をグラフ化する

棒グラフ(平均値の比較)

ここでは食性どうしを比べたいので,2節で見た <NA> の行を filter(!is.na(vore)) で除いてから集計します。

# 集計データを作成
sleep_summary <- msleep |>
  filter(!is.na(vore)) |>
  group_by(vore) |>
  summarise(
    mean_sleep = mean(sleep_total, na.rm = TRUE),
    sd_sleep   = sd(sleep_total, na.rm = TRUE),
    n          = n()
  )

# 棒グラフ(geom_col で集計済みデータを使う)
ggplot(sleep_summary, aes(x = vore, y = mean_sleep, fill = vore)) +
  geom_col() +
  scale_fill_brewer(palette = "Set2") +
  labs(title = "食性ごとの平均睡眠時間",
       x = "食性", y = "平均睡眠時間(時間)",
       fill = "食性") +
  theme_bw()

エラーバー付き棒グラフ

ggplot(sleep_summary, aes(x = vore, y = mean_sleep, fill = vore)) +
  geom_col() +
  geom_errorbar(aes(ymin = mean_sleep - sd_sleep,
                    ymax = mean_sleep + sd_sleep),
                width = 0.2) +
  scale_fill_brewer(palette = "Set2") +
  labs(title = "食性ごとの平均睡眠時間(±SD)",
       x = "食性", y = "平均睡眠時間(時間)") +
  theme_bw() +
  theme(legend.position = "none")


5. 欠損値の扱い

# 欠損値がある列の確認
msleep |>
  summarise(across(everything(), ~ sum(is.na(.))))
# 欠損値を除いて集計(na.rm = TRUE)
msleep |>
  summarise(mean_rem = mean(sleep_rem, na.rm = TRUE))

n() と na.rm = TRUE を並べるときは注意してください。

n() は行数を数えるだけで,欠損があるかどうかは見ていません。 一方 mean(x, na.rm = TRUE) は欠損を除いて計算します。 つまり同じ summarise() の中に並べると,n と「平均に使われた件数」がずれます。

msleep |>
  summarise(n = n(), mean_brain = mean(brainwt, na.rm = TRUE))
# n は 83 と出るが,平均に使われたのは 56 件(brainwt は27件が欠損)

実際に使われた件数を数えたいときは sum(!is.na(brainwt)) を使います。 レポートや論文で n を書くときは,どちらの数字なのかをはっきりさせてください。

# 欠損値のある行を除いてから集計
msleep |>
  filter(!is.na(sleep_rem)) |>
  group_by(vore) |>
  summarise(
    n = n(),
    mean_rem = mean(sleep_rem)
  )

6. 実際のCSVファイルの読み込み

ここまではRに最初から入っているデータを使ってきました。 自分で集めたデータを読み込むときは,次のように書きます。

# CSVファイルの読み込み(readr パッケージ,tidyverse に含まれる)
my_data <- read_csv("data/my_data.csv")

日本語が化けるとき ―― 文字コードの問題

read_csv() は UTF-8 という文字コードを前提にしています。 ところが Windows版のExcelで「CSV(コンマ区切り)」として保存すると,多くの場合 Shift_JIS(CP932)になります。 この食い違いが起きると,こうなります。

> my_data
  \x92n\x88\xe6 \x90l\x90\x94
1 \x93\x8c\x8b\x9e           10
2       \x8eD\x96y           20

本来は 地域 人数 東京 札幌 と表示されるはずのところが,\x で始まる記号の列になります。

これが一番やりにくい点です。 エラーで止まりません。 読み込み自体は成功してしまうので,気づかないまま集計や作図に進み, グラフの軸ラベルが記号だらけになって初めて気づく,ということが起きます。 読み込んだ直後に必ず中身を表示して,日本語が読めているか確認してください。

対処は2つあります。

① 読み込むときに文字コードを指定する

# Windows版Excelで保存したCSVは,たいていこちら
my_data <- read_csv("data/my_data.csv", locale = locale(encoding = "CP932"))

# すでにUTF-8になっているCSV(指定しなくても読めます)
my_data <- read_csv("data/my_data.csv", locale = locale(encoding = "UTF-8"))

② 保存するときにUTF-8にしておく(こちらを推奨)

Excelの「名前を付けて保存」で,ファイルの種類を CSV UTF-8 (コンマ区切り) にします。 こうしておけば read_csv() に何も足さずに読めますし,他の人とデータを共有するときも安全です。

どちらの文字コードか分からないときは,まず read_csv() だけで試してください。 化けていたら locale = locale(encoding = "CP932") を足す,という順番で構いません。

Excelファイルを直接読む方法もあります

.xlsx のまま読めば,文字コードの問題は起きません。

# install.packages("readxl")   # 最初の1回だけ
library(readxl)

my_data <- read_excel("data/my_data.xlsx")

# シートを指定する場合
my_data <- read_excel("data/my_data.xlsx", sheet = "データ")

Excelで管理しているデータなら,CSVに変換せずこちらのほうが確実です。

readxl は tidyverse に含まれていないので,install.packages("readxl") が別に必要です。


演習

演習1:グループ別集計

iris データを使って,種(Species)ごとに以下を計算してください。

  • Petal.Length の平均・標準偏差・サンプルサイズ
iris |>
  group_by(___) |>
  summarise(
    n    = n(),
    mean = mean(___),
    sd   = sd(___)
  )

演習2:集計結果のグラフ化

演習1で作った集計結果を使って,エラーバー付きの棒グラフを作成してください。

# まず集計
petal_summary <- iris |>
  group_by(Species) |>
  summarise(
    mean_petal = mean(Petal.Length),
    sd_petal   = sd(Petal.Length)
  )

# グラフ
ggplot(petal_summary, aes(x = ___, y = ___, fill = ___)) +
  geom_col() +
  geom_errorbar(aes(ymin = ___ - ___, ymax = ___ + ___), width = 0.2) +
  scale_fill_brewer(palette = "Set2") +
  labs(title = "種ごとの花びらの長さ(平均±SD)",
       x = "___", y = "___") +
  theme_bw()

演習3:count()の応用

msleep の食性(vore)ごとのサンプルサイズを count() で集計し,多い順に並べてください。

vore には欠損値(NA)が7件あります。count() はそのまま使うと NA も1つのカテゴリとして数えます。今回は NA を除いて集計してください。


本日のまとめ

  • summarise() でデータを集計できる
  • group_by() と組み合わせるとグループ別集計が可能
  • count() でカテゴリの頻度を数えられる
  • 集計結果を ggplot2 でグラフ化できる
  • na.rm = TRUE で欠損値を除いた計算ができる

宿題・予習

  • 本日の演習を仕上げ,スクリプトを保存しておく
  • 次回は習熟度確認テスト(第1回)と復習です
  • 第2〜6回で学んだ内容(可視化・加工)を見直しておく

← 授業トップへ戻る