SWELL公式サイトへ 詳しくはこちら

🧠 groupbyとpivot_table、結局どっちがスマート?Python集計処理のベストプラクティス

  • URLをコピーしました!
目次

🌟 Python集計でよくある「迷い」

Pythonでデータ集計をしていると、ふとこんな疑問が湧いてきます。

「この処理、groupbypivot_tableどっち使えばいいんだろう…?」

短くスマートに書きたい一方で、 「処理を追加したい」「欠損にも対応したい」となってくると、 見た目の短さだけでは済まされない現実が立ちはだかります。

この記事では、実際のコードとともに、 **“スマートな集計処理とは何か?”**を探り下げていきます。

✅ 3ステップ構成のgroupby集計(件数+点数合計)

まずは柔軟性重視のgroupby構成をご紹介します。

この構成では、以下の3つのステップで処理を行います。 それぞれの意図と期待する効果を簡単に整理すると:

🪜 ステップの目的

  1. ベースの一覧を作る:ID × 項目名のユニークな組み合わせを抽出し、結果表の骨組みを準備します。
    • ➤ 欠損や未出現の項目も「必ず表示」するための準備段階です。
  2. 集計処理を行う:内部コードが存在するデータだけを対象に、
    • 「内部コードの件数(≒出現数)」
    • 「点数列の合計(≒定量的評価)」 をgroupby+aggで一括集計します。
  3. 結合・補完して整える:最初に用意した骨組みに集計結果を左結合。
    • 欠損(=集計に該当しなかった)箇所は0で補完し、
    • 最終的に整形された一覧表として出力します。

この流れは、**「完全な出力表が欲しい」「集計後も追加加工したい」**という目的に非常に適しています。

# ステップ1: ID × 項目名のユニークな組み合わせをベースに
base_df = merged_df[["ID", "項目名"]].drop_duplicates()

# ステップ2: 内部コードが存在する行のみ対象に、件数+点数合計を集計
agg_df = (
    merged_df[merged_df["内部コード"].notna()]
    .groupby(["ID", "項目名"])
    .agg(
        件数=("内部コード", "count"),
        点数合計=("点数", "sum")
    )
    .reset_index()
)

# ステップ3: 欠損値を0で補筆して、ベースと結合
result_df = pd.merge(base_df, agg_df, on=["ID", "項目名"], how="left")
result_df["件数"] = result_df["件数"].fillna(0).astype(int)
result_df["点数合計"] = result_df["点数合計"].fillna(0).astype(float)

🔍 集計結果のイメージ

ID項目名件数点数合計
1薬剤A10450.0
2薬剤B00.0
3薬剤C3120.0

☑ ️ 「薬剤B」のように内部コードが欠損していた項目も、件数0&点数0.0で出力される のがこの構成の強み。

💡 ワトソンの集計Tips!

  1. countNaNをカウントしないので、想定する件数とのズレに注意
  2. sumNaNがあると合計もNaNになりがちfillna(0) を習慣に
  3. astype(int) は NaNを含むとエラーになるので、fillna後に型変換するのが安全
  4. .agg() の構文は、{"新しい列名": (元列名, 関数)} とすると合理的なアウトプットに

⛓️ 分析で使える集計関数まとめ

データの集計では、基本的な統計処理が求められます。 以下は groupby() などと一緒に使用できる代表的な集計関数です。

関数名説明使用例
count()要素の件数(NaN除く)トランザクション回数の集計
size()要素の件数(NaN含む)ログ件数や欠損も含めた計数
sum()合計値送金額の合計
mean()平均値1件あたりの平均送金額
max()最大値最大送金額
min()最小値最小送金額
nunique()ユニークな要素の数アドレス数、コントラクト数

✅ データは量が膨大なった場合、集計処理はできるだけ軽く・効率よく書くことが重要になります。

🚀 groupby 高速化テクニック3選

Pandasのgroupbyは便利ですが、大規模データでは遅くなることも。 以下の方法で処理速度を向上できます。

1. 列を必要なものだけに絞る

df = df[["ID", "項目名", "点数"]]  # 事前に必要な列だけ抽出

2. カテゴリ型に変換して処理軽量化

df["項目名"] = df["項目名"].astype("category")

3. 計算対象の行数を絞る(事前フィルタ)

filtered = df[df["内部コード"].notna()]  # 欠損除外を最初に

💡 高速化のポイントは、「使う列・行だけに限定して、無駄な処理を避ける」ことです。

🔁 pivot_table で書く場合(コンパクト重視系)

pivot_df = pd.pivot_table(
    data=merged_df[merged_df["内部コード"].notna()],
    index=["ID", "項目名"],
    values=["内部コード", "点数"],
    aggfunc={"内部コード": "count", "点数": "sum"},
    fill_value=0
).reset_index().rename(columns={"内部コード": "件数", "点数": "点数合計"})

📅 pivot_table の出力例

ID項目名件数点数合計
1薬剤A10450.0
3薬剤C3120.0

⚠️ pivot_table は、元データに内部コードが欠けている行は出力されない ので注意。

🔍 結局「スマート」とは何なのか?

目的最適な構文
缺損も含めて網羅的に管理✅ groupby + merge
簡易にまとめて出力✅ pivot_table
分布の傾向を確認✅ crosstab

スマートさは**「コードが短い」より、「拡張しやすい」「壊れにくい」**の方が重要な場面もあります。

🧪 おまけ:crosstabという選択肢も

pd.crosstab(
    index=[merged_df["ID"], merged_df["項目名"]],
    columns=merged_df["内部コード"].notna(),
    margins=False
)
  • 分布の確認やカテゴリの可視化に便利
  • ただし、柔軟な動程化には向かない

💟 結論:目的に合った“スマートさ”が最強

集計の目的ベストな方法
拡張性・保守性が大事✅ groupbyで構造的に書く
集計だけ済ませたい✅ pivot_tableで一発
分布の傾向を確認したい✅ crosstabでざっくり見る

✍️ 最後に:未来の自分が喜ぶコードを書こう

📌 スマートさは見た目だけじゃない。
「あとから見てもわかる」「ちょっと修正しやすい」
そんなコードこそ、現場で一番有難がられる。

よかったらシェアしてね!
  • URLをコピーしました!
目次