library(tidyverse)
data(iris)8 データハンドリング応用 - dplyr発展
前章でselect(), filter(), arrange()を学びました。この章では、さらに強力なデータ操作の方法を学びます。
実務でのデータ分析では、新しい列を作成したり、データを集計したりすることが頻繁にあります。dplyrのmutate(), summarize(), group_by()を使うと、これらの操作を簡単に実行できます。
8.1 学習内容
この章を読み終えると、以下ができるようになります。
mutate(): 新しい列を作成するsummarize(): データを要約するgroup_by(): グループごとに集計するcount(): データを数える- これらを組み合わせた実践的なデータ分析
8.2 データの準備
引き続きirisデータセットを使います。
8.3 mutate(): 新しい列を作成する
mutate()関数は、既存の列を使って新しい列を作成したり、既存の列を変換したりします。
8.3.1 基本的な使い方
mutate()内で新しい列名を指定し、その列に代入する値を計算式で書きます。
# がく片の面積を計算して新しい列として追加
result <- iris |>
mutate(Sepal.Area = Sepal.Length * Sepal.Width) |>
select(Species, Sepal.Length, Sepal.Width, Sepal.Area) |>
head()
head(result) Species Sepal.Length Sepal.Width Sepal.Area
1 setosa 5.1 3.5 17.85
2 setosa 4.9 3.0 14.70
3 setosa 4.7 3.2 15.04
4 setosa 4.6 3.1 14.26
5 setosa 5.0 3.6 18.00
6 setosa 5.4 3.9 21.06
8.3.2 複数の列を一度に作成
# 複数の計算を一度に実行
result <- iris |>
mutate(
Sepal.Area = Sepal.Length * Sepal.Width,
Petal.Area = Petal.Length * Petal.Width,
Total.Area = Sepal.Area + Petal.Area
) |>
select(Species, Sepal.Area, Petal.Area, Total.Area)
head(result) Species Sepal.Area Petal.Area Total.Area
1 setosa 17.85 0.28 18.13
2 setosa 14.70 0.28 14.98
3 setosa 15.04 0.26 15.30
4 setosa 14.26 0.30 14.56
5 setosa 18.00 0.28 18.28
6 setosa 21.06 0.68 21.74
8.3.3 条件による値の変更
if_else()やcase_when()を使うと、条件に応じて値を変更できます。
if_else()は、第1引数に条件、第2引数に条件がTRUEの場合の値、第3引数に条件がFALSEの場合の値を指定します。
# Sepal.Lengthが5以上なら"大きい"、そうでなければ"小さい"
result <- iris |>
mutate(
Size = if_else(Sepal.Length >= 5, "大きい", "小さい")
) |>
select(Species, Sepal.Length, Size)
head(result) Species Sepal.Length Size
1 setosa 5.1 大きい
2 setosa 4.9 小さい
3 setosa 4.7 小さい
4 setosa 4.6 小さい
5 setosa 5.0 大きい
6 setosa 5.4 大きい
複数の条件がある場合はcase_when()が便利です。TRUEは、上の条件に当てはまらない場合の値を指定するために使います。
# Sepal.Lengthを3段階に分類
iris |>
mutate(
Size_Category = case_when(
Sepal.Length < 5 ~ "小",
Sepal.Length < 6 ~ "中",
TRUE ~ "大" # それ以外(上の条件に当てはまらない場合)
)
) |>
select(Species, Sepal.Length, Size_Category) |>
head(10) Species Sepal.Length Size_Category
1 setosa 5.1 中
2 setosa 4.9 小
3 setosa 4.7 小
4 setosa 4.6 小
5 setosa 5.0 中
6 setosa 5.4 中
7 setosa 4.6 小
8 setosa 5.0 中
9 setosa 4.4 小
10 setosa 4.9 小
8.4 summarize(): データを要約する
summarize()(またはsummarise())関数は、データを集約して要約統計量を計算します。
mutate()の場合と同様に、summarize()内で新しい列名を指定し、その列に代入する値を計算式で書きます。
8.4.1 基本的な統計量
# がく片の長さの平均値を計算
iris |>
summarize(
mean = mean(Sepal.Length)
) mean
1 5.843333
8.4.2 複数の統計量を一度に計算
# 複数の統計量を計算
iris |>
summarize(
mean = mean(Sepal.Length),
median = median(Sepal.Length),
sd = sd(Sepal.Length),
minimum = min(Sepal.Length),
maximum = max(Sepal.Length),
n = n()
) mean median sd minimum maximum n
1 5.843333 5.8 0.8280661 4.3 7.9 150
mean(): 平均値median(): 中央値sd(): 標準偏差var(): 分散min(): 最小値max(): 最大値sum(): 合計n(): 行数を数えるn_distinct(): ユニークな値の数
8.5 group_by(): グループごとに集計する
group_by()とsummarize()を組み合わせると、グループごとの集計ができます。これが非常に強力です。
8.5.1 基本的な使い方
# 品種ごとにがく片の長さの平均を計算
iris |>
group_by(Species) |>
summarize(
mean = mean(Sepal.Length)
)# A tibble: 3 × 2
Species mean
<fct> <dbl>
1 setosa 5.01
2 versicolor 5.94
3 virginica 6.59
8.5.2 複数の統計量をグループごとに計算
# 品種ごとに複数の統計量を計算
iris |>
group_by(Species) |>
summarize(
n = n(),
mean = mean(Sepal.Length),
sd = sd(Sepal.Length),
minimum = min(Sepal.Length),
maximum = max(Sepal.Length)
)# A tibble: 3 × 6
Species n mean sd minimum maximum
<fct> <int> <dbl> <dbl> <dbl> <dbl>
1 setosa 50 5.01 0.352 4.3 5.8
2 versicolor 50 5.94 0.516 4.9 7
3 virginica 50 6.59 0.636 4.9 7.9
8.5.3 グループ化したまま新しい列を作成
mutate()と組み合わせると、グループごとの計算結果を元のデータに追加できます。
# 品種ごとの平均値を各行に追加
result <- iris |>
group_by(Species) |>
mutate(
Species_Mean = mean(Sepal.Length),
Diff_from_Mean = Sepal.Length - Species_Mean
) |>
select(Species, Sepal.Length, Species_Mean, Diff_from_Mean)
head(result)# A tibble: 6 × 4
# Groups: Species [1]
Species Sepal.Length Species_Mean Diff_from_Mean
<fct> <dbl> <dbl> <dbl>
1 setosa 5.1 5.01 0.0940
2 setosa 4.9 5.01 -0.106
3 setosa 4.7 5.01 -0.306
4 setosa 4.6 5.01 -0.406
5 setosa 5 5.01 -0.00600
6 setosa 5.4 5.01 0.394
ページの都合上Speciesがsetosaしか表示されていませんが、virginicaとversicolorも同様に平均値と平均との差分が計算されます。ポイントは、mutate()はグループ化した状態で計算するため、同じ品種の行はそれぞれ同じ平均値が入ることです。
group_by()を使った後、グループ化を解除したい場合はungroup()を使います。
iris |>
group_by(Species) |>
summarize(平均 = mean(Sepal.Length)) |>
ungroup() # グループ化を解除グループ化したまま次の操作を行うと、予期しない結果になることがあるため注意が必要です。グループ化して計算したい範囲が終わったら、ungroup()でグループ化を解除する習慣をつけましょう。
8.5.4 .by引数でグループ化と集計を同時に行う
group_by()とsummarize()を組み合わせる代わりに、summarize()の.by引数を使うと、グループ化と集計を同時に行うことができます。
関数内でのみグループ化が行われるため、先述したungroup()を使う必要もなく、コードがシンプルになります。
# .by引数を使ってグループ化と集計を同時に
iris |>
summarize(
n = n(),
mean = mean(Sepal.Length),
sd = sd(Sepal.Length),
minimum = min(Sepal.Length),
maximum = max(Sepal.Length),
.by = Species
) Species n mean sd minimum maximum
1 setosa 50 5.006 0.3524897 4.3 5.8
2 versicolor 50 5.936 0.5161711 4.9 7.0
3 virginica 50 6.588 0.6358796 4.9 7.9
ちなみに.by引数は、summarize()以外にもmutate()やfilter()などでも使えます。グループ化と集計を同時に行いたい場合は、.by引数を活用してコードをシンプルにすることも検討してみてください。
8.6 count(): データを数える
count()関数は、値の出現回数を簡単に数えられます。
8.6.1 基本的な使い方
以下の例では、Speciesごとにデータの数を数えています。
# 品種ごとのデータ数を数える
iris |>
count(Species) Species n
1 setosa 50
2 versicolor 50
3 virginica 50
これは以下と同じ意味です。
iris |>
group_by(Species) |>
summarize(n = n())# A tibble: 3 × 2
Species n
<fct> <int>
1 setosa 50
2 versicolor 50
3 virginica 50
8.7 実践例:売上データの分析
実務でよくある売上データの分析を例に、これまでの知識を総合的に使ってみましょう。
8.7.1 サンプルデータの作成
# 売上データの作成
sales <- data.frame(
日付 = rep(c("2025-01-01", "2025-01-02", "2025-01-03"), each = 4),
店舗 = rep(c("東京店", "大阪店", "東京店", "大阪店"), 3),
商品 = rep(c("商品A", "商品A", "商品B", "商品B"), 3),
売上数量 = c(10, 15, 8, 12, 12, 18, 10, 14, 15, 20, 12, 16),
単価 = c(1000, 1000, 1500, 1500, 1000, 1000, 1500, 1500, 1000, 1000, 1500, 1500)
)
print(sales) 日付 店舗 商品 売上数量 単価
1 2025-01-01 東京店 商品A 10 1000
2 2025-01-01 大阪店 商品A 15 1000
3 2025-01-01 東京店 商品B 8 1500
4 2025-01-01 大阪店 商品B 12 1500
5 2025-01-02 東京店 商品A 12 1000
6 2025-01-02 大阪店 商品A 18 1000
7 2025-01-02 東京店 商品B 10 1500
8 2025-01-02 大阪店 商品B 14 1500
9 2025-01-03 東京店 商品A 15 1000
10 2025-01-03 大阪店 商品A 20 1000
11 2025-01-03 東京店 商品B 12 1500
12 2025-01-03 大阪店 商品B 16 1500
8.7.2 分析1: 売上金額の計算
# 売上金額を計算
sales_with_amount <- sales |>
mutate(
売上金額 = 売上数量 * 単価
)
print(sales_with_amount) 日付 店舗 商品 売上数量 単価 売上金額
1 2025-01-01 東京店 商品A 10 1000 10000
2 2025-01-01 大阪店 商品A 15 1000 15000
3 2025-01-01 東京店 商品B 8 1500 12000
4 2025-01-01 大阪店 商品B 12 1500 18000
5 2025-01-02 東京店 商品A 12 1000 12000
6 2025-01-02 大阪店 商品A 18 1000 18000
7 2025-01-02 東京店 商品B 10 1500 15000
8 2025-01-02 大阪店 商品B 14 1500 21000
9 2025-01-03 東京店 商品A 15 1000 15000
10 2025-01-03 大阪店 商品A 20 1000 20000
11 2025-01-03 東京店 商品B 12 1500 18000
12 2025-01-03 大阪店 商品B 16 1500 24000
8.7.3 分析2: 店舗ごとの売上集計
# 店舗ごとの売上を集計
store_summary <- sales_with_amount |>
group_by(店舗) |>
summarize(
総売上数量 = sum(売上数量),
総売上金額 = sum(売上金額),
平均売上数量 = mean(売上数量)
# group_by()を使わない場合
# .by = 店舗
)
print(store_summary)# A tibble: 2 × 4
店舗 総売上数量 総売上金額 平均売上数量
<chr> <dbl> <dbl> <dbl>
1 大阪店 95 116000 15.8
2 東京店 67 82000 11.2
8.7.4 分析3: 商品・店舗ごとの売上集計
# 商品と店舗の組み合わせで集計
product_store_summary <- sales_with_amount |>
group_by(商品, 店舗) |>
summarize(
総売上金額 = sum(売上金額),
.groups = "drop" # グループ化を自動で解除
) |>
arrange(desc(総売上金額))
print(product_store_summary)# A tibble: 4 × 3
商品 店舗 総売上金額
<chr> <chr> <dbl>
1 商品B 大阪店 63000
2 商品A 大阪店 53000
3 商品B 東京店 45000
4 商品A 東京店 37000
8.7.5 分析4: 日付ごとの売上推移
# 日付ごとの売上推移
daily_sales <- sales_with_amount |>
group_by(日付) |>
summarize(
日次売上金額 = sum(売上金額),
日次販売数量 = sum(売上数量)
)
print(daily_sales)# A tibble: 3 × 3
日付 日次売上金額 日次販売数量
<chr> <dbl> <dbl>
1 2025-01-01 55000 45
2 2025-01-02 66000 54
3 2025-01-03 77000 63
8.8 応用テクニック
8.8.1 across()を使った複数列への同じ処理
複数の列に同じ処理を適用したい場合はacross()が便利です。
少々使い方は難しいですが、例えば数値列(numeric型)すべての平均を品種ごとに計算する場合は以下のように書けます。
# すべての数値列の平均を品種ごとに計算
iris |>
group_by(Species) |>
summarize(
across(where(is.numeric), mean)
)# A tibble: 3 × 5
Species Sepal.Length Sepal.Width Petal.Length Petal.Width
<fct> <dbl> <dbl> <dbl> <dbl>
1 setosa 5.01 3.43 1.46 0.246
2 versicolor 5.94 2.77 4.26 1.33
3 virginica 6.59 2.97 5.55 2.03
where()は条件に合う列を選択するための関数で、is.numericは数値列を選択する条件です。across()は、選択した列すべてに同じ関数(この場合はmean)を適用します。
たくさんの列に同じ処理を適用したい場合は、across()を使うとコードがシンプルになります。
8.8.2 slice()で行を選択
グループごとに上位N件を取得する場合はslice()が使えます。
# 品種ごとにSepal.Lengthが最も大きい3件を取得
iris |>
group_by(Species) |>
arrange(desc(Sepal.Length)) |>
slice(1:3)# A tibble: 9 × 5
# Groups: Species [3]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
<dbl> <dbl> <dbl> <dbl> <fct>
1 5.8 4 1.2 0.2 setosa
2 5.7 4.4 1.5 0.4 setosa
3 5.7 3.8 1.7 0.3 setosa
4 7 3.2 4.7 1.4 versicolor
5 6.9 3.1 4.9 1.5 versicolor
6 6.8 2.8 4.8 1.4 versicolor
7 7.9 3.8 6.4 2 virginica
8 7.7 3.8 6.7 2.2 virginica
9 7.7 2.6 6.9 2.3 virginica
SpeciesごとにSepal.Lengthが大きい順に並び替えた後、上位3件を取得しています。slice()は行番号で指定するため、1:3は上位3件を意味します。
ちなみにungroup()をしていないので、slice()はグループごとに行を選択します。グループ化していない場合は、全体の上位3件を取得することになります。
# 全体の上位3件を取得(グループ化していない場合)
iris |>
group_by(Species) |>
arrange(desc(Sepal.Length)) |>
ungroup() |>
slice(1:3)# A tibble: 3 × 5
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
<dbl> <dbl> <dbl> <dbl> <fct>
1 7.9 3.8 6.4 2 virginica
2 7.7 3.8 6.7 2.2 virginica
3 7.7 2.6 6.9 2.3 virginica
8.9 練習問題
irisデータで、がく片とFlowerの長さの合計をTotal.Lengthという列名で追加してください。
解答例
iris |>
mutate(Total.Length = Sepal.Length + Petal.Length) |>
select(Species, Sepal.Length, Petal.Length, Total.Length)irisデータで、品種ごとにPetal.Widthの平均、最小値、最大値を計算してください。
解答例
iris |>
group_by(Species) |>
summarize(
平均 = mean(Petal.Width),
最小値 = min(Petal.Width),
最大値 = max(Petal.Width)
)以下の従業員データで、部署ごとの平均年齢と売上合計を計算し、売上合計の降順に並び替えてください。
employees <- data.frame(
名前 = c("田中", "佐藤", "鈴木", "高橋", "伊藤", "渡辺"),
部署 = c("営業", "開発", "営業", "開発", "総務", "営業"),
年齢 = c(25, 30, 35, 28, 42, 31),
売上 = c(1200, 1500, 980, 1350, 1100, 1450)
)解答例
employees |>
group_by(部署) |>
summarize(
平均年齢 = mean(年齢),
売上合計 = sum(売上)
) |>
arrange(desc(売上合計))8.10 まとめ
この章では、dplyrの発展的な機能を学びました。
- ✅
mutate(): 新しい列の作成・既存列の変換 - ✅
summarize(): データの要約統計 - ✅
group_by(): グループごとの集計 - ✅
count(): データのカウント - ✅ これらを組み合わせた実践的なデータ分析
これで、データの読み込みから加工、集計まで一連の流れができるようになりました。次の章では、分析結果を視覚的に表現する方法を学びます。
次の章「データ可視化入門 - ggplot2基礎」では、ggplot2パッケージを使ったグラフ作成を学びます。データの特徴を視覚的に理解し、他者に伝える力を身につけましょう。