Pandas グループ操作(groupby)
groupbyは Pandas の最も強力な機能の一つであり、1つまたは複数の列に基づいてデータをグループ化し、各グループに対して集約・変換・フィルタリング操作を行うことができます。
groupby の基本用法
単一列グループ化
例
import pandas as pd
# サンプルデータを作成
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営", "営業", "技術"],
"氏名": ["张三", "李四", "王五", "赵六", "钱七", "孙八"],
"給与": [12000, 15000, 11000, 18000, 14000, 13000]
})
print("元データ:")
print(df)
print()
# 部門ごとにグループ化
grouped = df.groupby("部門")
print(f"グループオブジェクト: {type(grouped)}")
print(f"グループ数: {len(grouped)}")
print()
# グループを確認
print("各グループのデータ:")
for name, group in grouped:
print(f"\n部門: {name}")
print(group)
# サンプルデータを作成
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営", "営業", "技術"],
"氏名": ["张三", "李四", "王五", "赵六", "钱七", "孙八"],
"給与": [12000, 15000, 11000, 18000, 14000, 13000]
})
print("元データ:")
print(df)
print()
# 部門ごとにグループ化
grouped = df.groupby("部門")
print(f"グループオブジェクト: {type(grouped)}")
print(f"グループ数: {len(grouped)}")
print()
# グループを確認
print("各グループのデータ:")
for name, group in grouped:
print(f"\n部門: {name}")
print(group)
グループ化と集計
例
import pandas as pd
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営", "営業"],
"給与": [12000, 15000, 11000, 18000, 14000]
})
print(部門ごとの平均給与:)
print(df.groupby("部門")["給与"].mean())
print()
print(部門ごとの合計:)
print(df.groupby("部門")["給与"].sum())
print()
print(部門ごとの件数:)
print(df.groupby("部門").size())
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営", "営業"],
"給与": [12000, 15000, 11000, 18000, 14000]
})
print(部門ごとの平均給与:)
print(df.groupby("部門")["給与"].mean())
print()
print(部門ごとの合計:)
print(df.groupby("部門")["給与"].sum())
print()
print(部門ごとの件数:)
print(df.groupby("部門").size())
複数列グループ化
例
import pandas as pd
df = pd.DataFrame({
"年": ["2023", "2023", "2024", "2024"],
"部門": ["技術", "営業", "技術", "営業"],
"給与": [12000, 15000, 13000, 16000]
})
print(年と部門でグループ化:)
print(df.groupby(["年", "部門"])["給与"].sum())
print()
# DataFrame として出力
result = df.groupby(["年", "部門"]).agg({
"給与": "sum"
}).reset_index()
print(結果の DataFrame:)
print(result)
df = pd.DataFrame({
"年": ["2023", "2023", "2024", "2024"],
"部門": ["技術", "営業", "技術", "営業"],
"給与": [12000, 15000, 13000, 16000]
})
print(年と部門でグループ化:)
print(df.groupby(["年", "部門"])["給与"].sum())
print()
# DataFrame として出力
result = df.groupby(["年", "部門"]).agg({
"給与": "sum"
}).reset_index()
print(結果の DataFrame:)
print(result)
よく使う集計関数
単一の集計
| 関数 | 説明 |
|---|---|
sum() |
合計 |
mean() |
平均 |
median() |
中央値 |
std() |
標準偏差 |
min() / max() |
最小/最大値 |
count() |
カウント |
first() / last() |
最初/最後の値 |
例
import pandas as pd
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営"],
"給与": [12000, 15000, 11000, 18000]
})
# 複数の集計をチェーン呼び出し
print("チェーン集計:")
print(df.groupby("部門")["給与"].agg(["sum", "mean", "max", "min"]))
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営"],
"給与": [12000, 15000, 11000, 18000]
})
# 複数の集計をチェーン呼び出し
print("チェーン集計:")
print(df.groupby("部門")["給与"].agg(["sum", "mean", "max", "min"]))
カスタム集計
例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営"],
"給与": [12000, 15000, 11000, 18000]
})
# カスタム集計関数
def range_func(x):
return x.max() - x.min()
print("カスタム関数を使用:")
print(df.groupby("部門")["給与"].agg(range_func))
print()
# 名前付き集計(推奨)
print("名前付き集計:")
print(df.groupby("部門").agg(
最低給与=("給与", "min"),
最高給与=("給与", "max"),
平均給与=("給与", "mean")
))
import numpy as np
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営"],
"給与": [12000, 15000, 11000, 18000]
})
# カスタム集計関数
def range_func(x):
return x.max() - x.min()
print("カスタム関数を使用:")
print(df.groupby("部門")["給与"].agg(range_func))
print()
# 名前付き集計(推奨)
print("名前付き集計:")
print(df.groupby("部門").agg(
最低給与=("給与", "min"),
最高給与=("給与", "max"),
平均給与=("給与", "mean")
))
transform
transform は元のデータ形状を維持したまま、各グループに対して計算を行い、元のデータと同じ形状の結果を返すことができます。
例
import pandas as pd
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営", "営業"],
"給与": [12000, 15000, 11000, 18000, 14000]
})
print("元データ:")
print(df)
print()
# 各部門の平均給与を計算し、各行にブロードキャスト
df["部門平均給与"] = df.groupby("部門")["給与"].transform("mean")
print(部門平均給与を追加:)
print(df)
print()
# 各人の給与が部門に占める割合を計算
df["給与割合"] = df["給与"] / df["部門平均給与"]
print("給与割合:")
print(df)
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営", "営業"],
"給与": [12000, 15000, 11000, 18000, 14000]
})
print("元データ:")
print(df)
print()
# 各部門の平均給与を計算し、各行にブロードキャスト
df["部門平均給与"] = df.groupby("部門")["給与"].transform("mean")
print(部門平均給与を追加:)
print(df)
print()
# 各人の給与が部門に占める割合を計算
df["給与割合"] = df["給与"] / df["部門平均給与"]
print("給与割合:")
print(df)
filter フィルタリング
filter はグループ化条件に基づいてデータをフィルタリングできます。
例
import pandas as pd
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営", "営業", "技術"],
"給与": [12000, 15000, 11000, 18000, 14000, 13000]
})
print("元データ:")
print(df)
print()
# 部門平均給与が13000より大きい部門をフィルタリング
print("部門平均給与 > 13000:")
filtered = df.groupby("部門").filter(lambda x: x["給与"].mean() > 13000)
print(filtered)
print()
# メンバー数が2より大きいグループをフィルタリング
print("メンバー数 > 2:")
filtered2 = df.groupby("部門").filter(lambda x: len(x) > 2)
print(filtered2)
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営", "営業", "技術"],
"給与": [12000, 15000, 11000, 18000, 14000, 13000]
})
print("元データ:")
print(df)
print()
# 部門平均給与が13000より大きい部門をフィルタリング
print("部門平均給与 > 13000:")
filtered = df.groupby("部門").filter(lambda x: x["給与"].mean() > 13000)
print(filtered)
print()
# メンバー数が2より大きいグループをフィルタリング
print("メンバー数 > 2:")
filtered2 = df.groupby("部門").filter(lambda x: len(x) > 2)
print(filtered2)
apply カスタム関数
apply は各グループにカスタム関数を適用することができます。
例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営"],
"給与": [12000, 15000, 11000, 18000]
})
# 各グループにカスタム関数を適用
result = df.groupby("部門").apply(
lambda x: pd.Series({
"合計": x["給与"].sum(),
"平均": x["給与"].mean()
})
)
print("カスタム集計結果:")
print(result)
import numpy as np
df = pd.DataFrame({
"部門": ["技術", "営業", "技術", "運営"],
"給与": [12000, 15000, 11000, 18000]
})
# 各グループにカスタム関数を適用
result = df.groupby("部門").apply(
lambda x: pd.Series({
"合計": x["給与"].sum(),
"平均": x["給与"].mean()
})
)
print("カスタム集計結果:")
print(result)
実践:売上データ分析
例
import pandas as pd
# 売上データをシミュレート
sales = pd.DataFrame({
"日付": pd.date_range("2024-01-01", periods=30, freq="D"),
"製品": ["スマートフォン", "パソコン", "タブレット"] * 10,
"チャネル": ["オンライン"] * 15 + ["オフライン"] * 15,
"売上額": [1000, 2000, 1500] * 10
})
print("=== 売上データ分析 ===\n")
# 1. 製品別に集計
print("1. 製品別集計:")
product_summary = sales.groupby("製品")["売上額"].agg(["sum", "mean", "count"])
print(product_summary)
print()
# 2. チャネル別に集計
print("2. チャネル別集計:")
channel_summary = sales.groupby("チャネル")["売上額"].sum()
print(channel_summary)
print()
# 3. 製品とチャネルのクロス集計
print("3. 製品×チャネルクロス集計:")
cross_summary = sales.groupby(["製品", "チャネル"])["売上額"].sum().unstack()
print(cross_summary)
print()
# 4. 各製品の売上割合を計算
print("4. 売上割合:")
total = sales["売上額"].sum()
product_pct = sales.groupby("製品")["売上額"].sum() / total * 100
print(product_pct.round(2))
# 売上データをシミュレート
sales = pd.DataFrame({
"日付": pd.date_range("2024-01-01", periods=30, freq="D"),
"製品": ["スマートフォン", "パソコン", "タブレット"] * 10,
"チャネル": ["オンライン"] * 15 + ["オフライン"] * 15,
"売上額": [1000, 2000, 1500] * 10
})
print("=== 売上データ分析 ===\n")
# 1. 製品別に集計
print("1. 製品別集計:")
product_summary = sales.groupby("製品")["売上額"].agg(["sum", "mean", "count"])
print(product_summary)
print()
# 2. チャネル別に集計
print("2. チャネル別集計:")
channel_summary = sales.groupby("チャネル")["売上額"].sum()
print(channel_summary)
print()
# 3. 製品とチャネルのクロス集計
print("3. 製品×チャネルクロス集計:")
cross_summary = sales.groupby(["製品", "チャネル"])["売上額"].sum().unstack()
print(cross_summary)
print()
# 4. 各製品の売上割合を計算
print("4. 売上割合:")
total = sales["売上額"].sum()
product_pct = sales.groupby("製品")["売上額"].sum() / total * 100
print(product_pct.round(2))
その他の拡張groupby はデータ分析の中核となる操作であり、使いこなすことでさまざまな統計分析タスクを効率的に完了できます。