📊 ステップ33: ピボットテーブルを作ろう
データを表形式で集計して、見やすく分析しよう!
ステップ32では、重複データの処理を学びました。今回は、データを表形式で集計するピボットテーブルの作り方を学びます。
📖 このステップで学ぶこと
・pivot_table()の使い方
・行・列・値の指定
・集計関数の選択
・クロス集計の作成
🎯 1. ピボットテーブルとは?
ピボットテーブルは、データを縦軸と横軸で整理して集計する機能です。Excelのピボットテーブルと同じ考え方です。
🔰 ピボットテーブルのイメージ
📌 ピボットテーブルの例
売上データから、商品×地域の売上合計を表形式で表示
・縦軸(行):商品(りんご、バナナ、みかん)
・横軸(列):地域(東京、大阪、名古屋)
・値:売上金額の合計
これにより、「どの商品がどの地域で売れているか」が一目でわかります!
📘 サンプルデータを準備
コード:サンプルデータの作成
import pandas as pd
# 売上データ
sales_data = {
'日付': ['1/1', '1/1', '1/2', '1/2', '1/3', '1/3', '1/1', '1/2'],
'商品': ['りんご', 'バナナ', 'りんご', 'みかん', 'バナナ', 'りんご',
'みかん', 'バナナ'],
'地域': ['東京', '大阪', '東京', '名古屋', '大阪', '東京',
'東京', '名古屋'],
'売上': [1200, 900, 1500, 800, 1100, 1000, 750, 950]
}
df = pd.DataFrame(sales_data)
print("売上データ:")
print(df)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
売上データ: 日付 商品 地域 売上 0 1/1 りんご 東京 1200 1 1/1 バナナ 大阪 900 2 1/2 りんご 東京 1500 3 1/2 みかん 名古屋 800 4 1/3 バナナ 大阪 1100 5 1/3 りんご 東京 1000 6 1/1 みかん 東京 750 7 1/2 バナナ 名古屋 950
💡 このデータから知りたいこと
・商品ごとの売上合計は?
・地域ごとの売上合計は?
・商品×地域の組み合わせでの売上は?
ピボットテーブルを使えば、これらを表形式で見やすく表示できます。
📊 2. pivot_table()の基本
pivot_table()は、DataFrameをピボットテーブル形式に変換するメソッドです。
🔰 最初のピボットテーブル
コード:商品×地域の売上合計
# 商品×地域の売上合計
pivot1 = df.pivot_table(
values='売上', # 集計する値
index='商品', # 行(縦軸)
columns='地域', # 列(横軸)
aggfunc='sum' # 集計方法(合計)
)
print("商品×地域の売上合計:")
print(pivot1)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
商品×地域の売上合計: 地域 名古屋 大阪 東京 商品 みかん 800.0 NaN 750.0 バナナ 950.0 2000.0 NaN りんご NaN NaN 3700.0
💡 コードの解説
df.pivot_table(…)
・DataFrameをピボットテーブルに変換します
values=’売上’
・集計する値の列を指定
・「売上」列の値を集計します
index=’商品’
・行(縦軸)に配置する列を指定
・商品名が行の見出しになります
columns=’地域’
・列(横軸)に配置する列を指定
・地域名が列の見出しになります
aggfunc=’sum’
・集計方法を指定(’sum’は合計)
・同じ組み合わせの値を合計します
📌 pivot_table()のパラメータまとめ
| パラメータ | 説明 | 例 |
| values | 集計する値の列 | values=’売上’ |
| index | 行(縦軸)に配置する列 | index=’商品’ |
| columns | 列(横軸)に配置する列 | columns=’地域’ |
| aggfunc | 集計方法 | aggfunc=’sum’ |
📝 NaNを0に変換
データがない組み合わせはNaN(欠損値)になります。fill_valueで埋められます。
コード:NaNを0に変換
# fill_valueでNaNを0に
pivot2 = df.pivot_table(
values='売上',
index='商品',
columns='地域',
aggfunc='sum',
fill_value=0
)
print("NaNを0に変換:")
print(pivot2)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
NaNを0に変換: 地域 名古屋 大阪 東京 商品 みかん 800 0 750 バナナ 950 2000 0 りんご 0 0 3700
💡 fill_valueの効果
fill_value=0
・NaN(データなし)を0で埋めます
・表が見やすくなります
・計算にも使いやすくなります
📘 合計行・列を追加
margins=Trueで、合計行と合計列を追加できます。
コード:合計行・列を追加
# margins=Trueで合計を追加
pivot3 = df.pivot_table(
values='売上',
index='商品',
columns='地域',
aggfunc='sum',
fill_value=0,
margins=True,
margins_name='合計'
)
print("合計行・列を追加:")
print(pivot3)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
合計行・列を追加: 地域 名古屋 大阪 東京 合計 商品 みかん 800 0 750 1550 バナナ 950 2000 0 2950 りんご 0 0 3700 3700 合計 1750 2000 4450 8200
💡 margins パラメータ
margins=True
・合計行と合計列を追加します
margins_name=’合計’
・合計行・列の名前を指定(デフォルトは’All’)
結果の見方:
・各商品の合計売上がわかる(りんご: 3700円)
・各地域の合計売上がわかる(東京: 4450円)
・全体の合計売上がわかる(8200円)
🎨 3. 様々な集計方法
aggfuncパラメータで、様々な集計方法を選べます。
📝 平均値で集計
コード:平均値を計算
# 平均値
avg_pivot = df.pivot_table(
values='売上',
index='商品',
columns='地域',
aggfunc='mean',
fill_value=0
)
print("商品×地域の平均売上:")
print(avg_pivot.round(0))
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
商品×地域の平均売上: 地域 名古屋 大阪 東京 商品 みかん 800.0 0.0 750.0 バナナ 950.0 1000.0 0.0 りんご 0.0 0.0 1233.0
💡 ‘mean’の意味
aggfunc=’mean’
・同じ組み合わせの値を平均します
・東京のりんごは3回売れて、合計3700円なので平均1233円
📝 件数を集計
コード:件数をカウント
# 件数(カウント)
count_pivot = df.pivot_table(
values='売上',
index='商品',
columns='地域',
aggfunc='count',
fill_value=0
)
print("商品×地域の販売回数:")
print(count_pivot)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
商品×地域の販売回数: 地域 名古屋 大阪 東京 商品 みかん 1 0 1 バナナ 1 2 0 りんご 0 0 3
💡 ‘count’の意味
aggfunc=’count’
・同じ組み合わせのデータ数を数えます
・東京でりんごは3回売れた
・大阪でバナナは2回売れた
📘 複数の集計を同時に
aggfuncにリストを渡すと、複数の集計を同時にできます。
コード:複数の集計方法
# 複数の集計方法
multi_pivot = df.pivot_table(
values='売上',
index='商品',
columns='地域',
aggfunc=['sum', 'mean', 'count'],
fill_value=0
)
print("複数の集計:")
print(multi_pivot)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
複数の集計:
sum mean count
地域 名古屋 大阪 東京 名古屋 大阪 東京 名古屋 大阪 東京
商品
みかん 800 0 750 800.0 0.0 750.0 1 0 1
バナナ 950 2000 0 950.0 1000.0 0.0 1 2 0
りんご 0 0 3700 0.0 0.0 1233.3 0 0 3
📌 主な集計関数(aggfunc)
| aggfunc | 説明 |
| ‘sum’ | 合計 |
| ‘mean’ | 平均 |
| ‘count’ | 件数 |
| ‘max’ | 最大値 |
| ‘min’ | 最小値 |
| ‘std’ | 標準偏差 |
🔢 4. 複数の軸での集計
📘 複数の行で集計
indexにリストを渡すと、複数の列を行に配置できます。
コード:日付×商品×地域の売上
# 日付×商品×地域
pivot_3d = df.pivot_table(
values='売上',
index=['日付', '商品'],
columns='地域',
aggfunc='sum',
fill_value=0
)
print("日付×商品×地域の売上:")
print(pivot_3d)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
日付×商品×地域の売上:
地域 名古屋 大阪 東京
日付 商品
1/1 みかん 0 0 750
バナナ 0 900 0
りんご 0 0 1200
1/2 みかん 800 0 0
バナナ 950 0 0
りんご 0 0 1500
1/3 バナナ 0 1100 0
りんご 0 0 1000
💡 複数列をindexに指定
index=[‘日付’, ‘商品’]
・日付と商品の組み合わせが行になります
・階層的なインデックスが作成されます
・より詳細な分析ができます
📘 複数の値を集計
valuesにリストを渡すと、複数の列を同時に集計できます。
コード:売上と数量を同時に集計
# 数量列を追加
df['数量'] = [10, 6, 12, 8, 7, 8, 5, 6]
# 売上と数量を同時に集計
multi_value_pivot = df.pivot_table(
values=['売上', '数量'],
index='商品',
columns='地域',
aggfunc='sum',
fill_value=0
)
print("売上と数量を同時に集計:")
print(multi_value_pivot)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
売上と数量を同時に集計:
売上 数量
地域 名古屋 大阪 東京 名古屋 大阪 東京
商品
みかん 800 0 750 8 0 5
バナナ 950 2000 0 6 13 0
りんご 0 0 3700 0 0 30
📋 5. クロス集計(crosstab)
pd.crosstab()は、2つの列の組み合わせの件数を集計する簡単な方法です。
🔰 crosstab()の基本
コード:クロス集計
# クロス集計
crosstab1 = pd.crosstab(df['商品'], df['地域'])
print("商品×地域のクロス集計:")
print(crosstab1)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
商品×地域のクロス集計: 地域 名古屋 大阪 東京 商品 みかん 1 0 1 バナナ 1 2 0 りんご 0 0 3
💡 crosstab()の使い方
pd.crosstab(行, 列)
・第1引数:行に配置する列
・第2引数:列に配置する列
・デフォルトで件数をカウントします
pivot_table()との違い:
・crosstab():件数を数えるのに特化
・pivot_table():様々な値を集計できる
📝 合計を追加
コード:合計行・列を追加
# 合計行・列を追加
crosstab2 = pd.crosstab(
df['商品'],
df['地域'],
margins=True,
margins_name='合計'
)
print("合計付きクロス集計:")
print(crosstab2)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
合計付きクロス集計: 地域 名古屋 大阪 東京 合計 商品 みかん 1 0 1 2 バナナ 1 2 0 3 りんご 0 0 3 3 合計 2 2 4 8
📘 割合を表示
normalizeパラメータで、割合(パーセント)を計算できます。
コード:割合を表示
# 割合で表示(全体に対する割合)
crosstab_pct = pd.crosstab(
df['商品'],
df['地域'],
normalize='all' # 全体に対する割合
) * 100
print("割合(%):")
print(crosstab_pct.round(1))
# 行ごとの割合
crosstab_row_pct = pd.crosstab(
df['商品'],
df['地域'],
normalize='index' # 行ごとの割合
) * 100
print("\n行ごとの割合(%):")
print(crosstab_row_pct.round(1))
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
割合(%): 地域 名古屋 大阪 東京 商品 みかん 12.5 0.0 12.5 バナナ 12.5 25.0 0.0 りんご 0.0 0.0 37.5 行ごとの割合(%): 地域 名古屋 大阪 東京 商品 みかん 50.0 0.0 50.0 バナナ 33.3 66.7 0.0 りんご 0.0 0.0 100.0
💡 normalizeパラメータ
normalize=’all’:全体に対する割合
normalize=’index’:行ごとの割合(各商品の地域別構成比)
normalize=’columns’:列ごとの割合(各地域の商品別構成比)
* 100:0〜1の値を0〜100%に変換
💼 6. 実践例
📘 例1:月次売上レポート
コード:月次売上レポートの作成
import pandas as pd
# 月次データ
monthly_data = pd.DataFrame({
'月': ['1月', '1月', '1月', '2月', '2月', '2月', '3月', '3月', '3月'],
'商品': ['A', 'B', 'C', 'A', 'B', 'C', 'A', 'B', 'C'],
'売上': [100, 150, 120, 110, 160, 130, 105, 155, 125]
})
# 月×商品のピボットテーブル
monthly_pivot = monthly_data.pivot_table(
values='売上',
index='商品',
columns='月',
aggfunc='sum',
margins=True,
margins_name='合計'
)
print("月次売上レポート:")
print(monthly_pivot)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
月次売上レポート: 月 1月 2月 3月 合計 商品 A 100 110 105 315 B 150 160 155 465 C 120 130 125 375 合計 370 400 385 1155
📘 例2:構成比の計算
コード:地域ごとの商品構成比
# 商品×地域の売上ピボット
sales_pivot = df.pivot_table(
values='売上',
index='商品',
columns='地域',
aggfunc='sum',
fill_value=0
)
print("売上ピボット:")
print(sales_pivot)
# 地域ごとの構成比を計算
# 各地域の合計で割る
composition = sales_pivot.div(
sales_pivot.sum(axis=0), # 各列(地域)の合計
axis=1 # 列方向で割る
) * 100
print("\n地域ごとの商品構成比(%):")
print(composition.round(1))
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
売上ピボット: 地域 名古屋 大阪 東京 商品 みかん 800 0 750 バナナ 950 2000 0 りんご 0 0 3700 地域ごとの商品構成比(%): 地域 名古屋 大阪 東京 商品 みかん 45.7 0.0 16.9 バナナ 54.3 100.0 0.0 りんご 0.0 0.0 83.1
💡 構成比の計算方法
sales_pivot.div(sales_pivot.sum(axis=0), axis=1)
・sales_pivot.sum(axis=0):各列(地域)の合計を計算
・.div(…):割り算
・axis=1:列方向で割る
結果の見方:
・東京ではりんごが83.1%を占める
・大阪ではバナナが100%を占める
📝 練習問題
ここまで学んだことを、実際に手を動かして確認しましょう。
問題1:基本的なピボットテーブル(初級)
📋 問題
以下のデータから、商品×地域の売上合計をピボットテーブルで作成してください。
df = pd.DataFrame({
'商品': ['A', 'B', 'A', 'C', 'B', 'C'],
'地域': ['東京', '大阪', '大阪', '東京', '東京', '大阪'],
'売上': [100, 150, 120, 90, 140, 110]
})
※ 画面が小さい場合は、コードブロックを横にスクロールできます
解答例を見る
コード
import pandas as pd
df = pd.DataFrame({
'商品': ['A', 'B', 'A', 'C', 'B', 'C'],
'地域': ['東京', '大阪', '大阪', '東京', '東京', '大阪'],
'売上': [100, 150, 120, 90, 140, 110]
})
print("元のデータ:")
print(df)
# ピボットテーブル
pivot = df.pivot_table(
values='売上',
index='商品',
columns='地域',
aggfunc='sum',
fill_value=0
)
print("\n商品×地域の売上合計:")
print(pivot)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
元のデータ: 商品 地域 売上 0 A 東京 100 1 B 大阪 150 2 A 大阪 120 3 C 東京 90 4 B 東京 140 5 C 大阪 110 商品×地域の売上合計: 地域 大阪 東京 商品 A 120 100 B 150 140 C 110 90
問題2:合計行・列を追加(初級)
📋 問題
問題1のピボットテーブルに、合計行と合計列を追加してください。
解答例を見る
コード
# margins=Trueで合計を追加
pivot_with_total = df.pivot_table(
values='売上',
index='商品',
columns='地域',
aggfunc='sum',
fill_value=0,
margins=True,
margins_name='合計'
)
print("合計行・列を追加:")
print(pivot_with_total)
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
合計行・列を追加: 地域 大阪 東京 合計 商品 A 120 100 220 B 150 140 290 C 110 90 200 合計 380 330 710
問題3:平均値のピボットテーブル(中級)
📋 問題
以下のデータから、クラス×科目の平均点をピボットテーブルで作成してください。
df = pd.DataFrame({
'クラス': ['A', 'A', 'B', 'B', 'A', 'B'],
'科目': ['数学', '英語', '数学', '英語', '数学', '数学'],
'点数': [85, 90, 78, 88, 92, 82]
})
※ 画面が小さい場合は、コードブロックを横にスクロールできます
解答例を見る
コード
import pandas as pd
df = pd.DataFrame({
'クラス': ['A', 'A', 'B', 'B', 'A', 'B'],
'科目': ['数学', '英語', '数学', '英語', '数学', '数学'],
'点数': [85, 90, 78, 88, 92, 82]
})
print("元のデータ:")
print(df)
# 平均点のピボットテーブル
pivot_avg = df.pivot_table(
values='点数',
index='クラス',
columns='科目',
aggfunc='mean',
fill_value=0
)
print("\nクラス×科目の平均点:")
print(pivot_avg.round(1))
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
元のデータ: クラス 科目 点数 0 A 数学 85 1 A 英語 90 2 B 数学 78 3 B 英語 88 4 A 数学 92 5 B 数学 82 クラス×科目の平均点: 科目 数学 英語 クラス A 88.5 90.0 B 80.0 88.0
問題4:クロス集計(中級)
📋 問題
以下のアンケートデータから、年齢層×満足度のクロス集計を作成してください(件数と割合)。
df = pd.DataFrame({
'年齢層': ['20代', '30代', '20代', '40代', '30代', '20代', '40代', '30代'],
'満足度': ['満足', '満足', '普通', '満足', '不満', '満足', '普通', '満足']
})
※ 画面が小さい場合は、コードブロックを横にスクロールできます
解答例を見る
コード
import pandas as pd
df = pd.DataFrame({
'年齢層': ['20代', '30代', '20代', '40代', '30代', '20代', '40代', '30代'],
'満足度': ['満足', '満足', '普通', '満足', '不満', '満足', '普通', '満足']
})
print("元のデータ:")
print(df)
# 件数のクロス集計
crosstab_count = pd.crosstab(
df['年齢層'],
df['満足度'],
margins=True,
margins_name='合計'
)
print("\n件数:")
print(crosstab_count)
# 割合(行ごと)
crosstab_pct = pd.crosstab(
df['年齢層'],
df['満足度'],
normalize='index'
) * 100
print("\n割合(%):")
print(crosstab_pct.round(1))
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
元のデータ: 年齢層 満足度 0 20代 満足 1 30代 満足 2 20代 普通 3 40代 満足 4 30代 不満 5 20代 満足 6 40代 普通 7 30代 満足 件数: 満足度 不満 普通 満足 合計 年齢層 20代 0 1 2 3 30代 1 0 2 3 40代 0 1 1 2 合計 1 2 5 8 割合(%): 満足度 不満 普通 満足 年齢層 20代 0.0 33.3 66.7 30代 33.3 0.0 66.7 40代 0.0 50.0 50.0
問題5:総合演習(上級)
📋 問題
以下のデータから:
1. 月×商品の売上合計(合計付き)
2. 月×商品の販売件数
3. 各月の商品別構成比(%)
4. 最も売上が高い月と商品を表示
df = pd.DataFrame({
'月': ['1月', '1月', '1月', '2月', '2月', '2月', '3月', '3月', '3月'],
'商品': ['A', 'B', 'C', 'A', 'B', 'C', 'A', 'B', 'C'],
'売上': [100, 150, 120, 110, 160, 130, 105, 155, 125]
})
※ 画面が小さい場合は、コードブロックを横にスクロールできます
解答例を見る
コード
import pandas as pd
df = pd.DataFrame({
'月': ['1月', '1月', '1月', '2月', '2月', '2月', '3月', '3月', '3月'],
'商品': ['A', 'B', 'C', 'A', 'B', 'C', 'A', 'B', 'C'],
'売上': [100, 150, 120, 110, 160, 130, 105, 155, 125]
})
print("="*50)
print("売上分析ダッシュボード")
print("="*50)
# 1. 売上合計
sales_pivot = df.pivot_table(
values='売上',
index='商品',
columns='月',
aggfunc='sum',
margins=True,
margins_name='合計'
)
print("\n【1. 月×商品の売上合計】")
print(sales_pivot)
# 2. 販売件数
count_pivot = df.pivot_table(
values='売上',
index='商品',
columns='月',
aggfunc='count',
fill_value=0
)
print("\n【2. 月×商品の販売件数】")
print(count_pivot)
# 3. 構成比
sales_no_margin = df.pivot_table(
values='売上',
index='商品',
columns='月',
aggfunc='sum'
)
composition = sales_no_margin.div(
sales_no_margin.sum(axis=0),
axis=1
) * 100
print("\n【3. 各月の商品別構成比(%)】")
print(composition.round(1))
# 4. 最高売上
max_idx = df['売上'].idxmax()
max_row = df.loc[max_idx]
print(f"\n【4. 最も売上が高い】")
print(f"{max_row['月']}の商品{max_row['商品']}: {max_row['売上']}円")
※ 画面が小さい場合は、コードブロックを横にスクロールできます
実行結果
================================================== 売上分析ダッシュボード ================================================== 【1. 月×商品の売上合計】 月 1月 2月 3月 合計 商品 A 100 110 105 315 B 150 160 155 465 C 120 130 125 375 合計 370 400 385 1155 【2. 月×商品の販売件数】 月 1月 2月 3月 商品 A 1 1 1 B 1 1 1 C 1 1 1 【3. 各月の商品別構成比(%)】 月 1月 2月 3月 商品 A 27.0 27.5 27.3 B 40.5 40.0 40.3 C 32.4 32.5 32.5 【4. 最も売上が高い】 2月の商品B: 160円
🎯 このステップのまとめ
✅ 学んだこと
✓ pivot_table()で表形式の集計ができる
✓ values, index, columnsで行・列・値を指定できる
✓ aggfuncで様々な集計関数が使える
✓ marginsで合計行・列を追加できる
✓ crosstab()でクロス集計ができる
✓ 構成比や割合を計算できる
💡 次のステップに進む前に確認
以下のことができるようになったか確認しましょう:
□ pivot_table()でピボットテーブルを作れる
□ 様々な集計関数を使える
□ 合計行・列を追加できる
□ crosstab()でクロス集計ができる
これらができたら、次のステップに進みましょう!
❓ よくある質問
Q1: pivot_table()とgroupby()の違いは?
A: groupby()は集計結果を縦に並べます。
pivot_table()は表形式(縦×横)で表示します。
見やすさが大きく違います。ピボットテーブルはExcelのように見やすい!
Q2: NaNが表示されてしまいます
A: fill_value=0を指定すると、NaNを0に変換できます。
見やすくなり、計算にも使いやすくなります。
Q3: 複数の集計をきれいに表示したいです
A: aggfuncにリストで複数指定できます:
aggfunc=['sum', 'mean', 'count']
ただし列が階層的になるので、見にくい場合は別々に作成することをお勧めします。
Q4: ピボットテーブルをExcelに出力できますか?
A: はい、pivot.to_excel('output.xlsx')で出力できます。
さらに整形したい場合は、openpyxlやxlsxwriterを使います。
Q5: pivot_table()とcrosstab()はどう使い分けますか?
A: pivot_table():数値の集計(売上合計など)
crosstab():件数の集計やカテゴリの組み合わせ
どちらでもできますが、crosstab()の方が件数を数えるのに簡潔です。
学習メモ
Pythonデータ分析入門 - Step 33