Excel・文書業務

pandas の groupby で月次集計を作成し、合計値を確認する

注文単位の CSV データを pandas で月ごとに集計し、集計後の件数、数量、金額を元データと照合します。小さな合成例を使うため、想定結果をすべて手作業で簡単に検証できます。

目次を表示

この翻訳はAIで作成しました。コード、単位、数値は原文と併せて確認してください。各言語のネイティブ話者による校閲は、まだ完了していません。 English

対象読者再現可能な月次 CSV 集計が必要で、集計処理によって records が失われていないことを明示的に確認したい人向けのガイドです。

準備するもの
  • Python 3.12 と、そのバージョンを起動できるターミナルコマンド。
  • python -m pip install pandas で pandas をインストール済みであること。
  • スクリプトが outputs の下に新しいフォルダを作成できる作業フォルダ。
  • 元の CSV には order_id, order_date, units, amount columns が必要です。

01月次指標を定義する

この例では、calendar month ごとに1行を作成し、order count、total units、total amount の3つの指標を集計します。合成の元データでは1行が1 order なので、この特定の dataset では行数を数えることが order 数を数えることと同じです。

重要なのは reconciliation です。groupby の後で月次値をもう一度合計し、元データの totals と比較します。summary が妥当に見えても、records が誤って filter, duplicate, drop されている可能性があります。

02合成 CSV を作成する

以下の dataset は合成データであり、この記事のために作成したものです。monthly_orders.csv として保存してください。2026年1月、2月、3月にまたがる7件の orders が含まれています。

csv
order_id,order_date,team,units,amount
O001,2026-01-05,North,2,40
O002,2026-01-20,South,3,60
O003,2026-02-02,North,1,25
O004,2026-02-18,South,4,100
O005,2026-02-28,North,2,50
O006,2026-03-03,South,5,125
O007,2026-03-15,North,1,30

元データの totals は手作業でも簡単に確認できます。orders は7件です。units の合計は 18: 2 + 3 + 1 + 4 + 2 + 5 + 1。amount の合計は 430: 40 + 60 + 25 + 100 + 50 + 125 + 30。

指標元データ合計
Orders7
Units18
Amount430

03想定される月次行を手作業で計算する

1月には O001 と O002、2月には O003, O004, O005、3月には O006 と O007 が含まれます。各 group を合計すると、想定される月次表が得られます。

monthordersunitsamount
2026-0125100
2026-0237175
2026-0326155

月次 totals は元データと一致します: 2 + 3 + 2 = 7 orders、5 + 7 + 6 = 18 units、100 + 175 + 155 = 430 amount。

04pandas で集計を作成し、検証する

次のスクリプトを monthly_summary_pandas.py として保存してください。required columns を検証し、missing または duplicate order IDs を拒否し、1つの明示的な形式で dates を解析し、numeric columns を変換し、month ごとに group 化し、totals を reconcile して、それらの check に合格した後だけ結果を書き出します。

python
from pathlib import Path
import pandas as pd

SOURCE = Path("monthly_orders.csv")
OUTPUT_DIR = Path("outputs") / "monthly_summary_result"
OUTPUT = OUTPUT_DIR / "monthly_summary.csv"
REQUIRED = ["order_id", "order_date", "units", "amount"]


def main() -> None:
    if not SOURCE.is_file():
        raise FileNotFoundError(f"Source CSV not found: {SOURCE}")
    if OUTPUT_DIR.exists():
        raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")

    data = pd.read_csv(SOURCE, dtype={"order_id": "string"})

    missing = [name for name in REQUIRED if name not in data.columns]
    if missing:
        raise ValueError(f"Missing required columns: {missing}")
    if data.empty:
        raise ValueError("Source CSV contains no data rows.")
    if data[REQUIRED].isna().any().any():
        raise ValueError("A required field contains a missing value.")
    if data["order_id"].str.strip().eq("").any():
        raise ValueError("order_id contains a blank value.")
    if data["order_id"].duplicated().any():
        raise ValueError("Duplicate order_id found.")

    data["order_date"] = pd.to_datetime(
        data["order_date"], format="%Y-%m-%d", errors="raise"
    )
    data["units"] = pd.to_numeric(data["units"], errors="raise")
    data["amount"] = pd.to_numeric(data["amount"], errors="raise")
    data["month"] = data["order_date"].dt.to_period("M").astype(str)

    summary = (
        data.groupby("month", as_index=False, sort=True)
        .agg(
            orders=("order_id", "count"),
            units=("units", "sum"),
            amount=("amount", "sum"),
        )
    )

    source_totals = (len(data), data["units"].sum(), data["amount"].sum())
    summary_totals = (
        summary["orders"].sum(),
        summary["units"].sum(),
        summary["amount"].sum(),
    )
    if summary_totals != source_totals:
        raise RuntimeError("Monthly totals do not reconcile with the source.")

    OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_DIR.mkdir()
    summary.to_csv(OUTPUT, index=False)

    check = pd.read_csv(OUTPUT)
    if check.to_dict("records") != summary.to_dict("records"):
        raise RuntimeError("Saved CSV does not match the calculated summary.")

    print(f"Source rows: {len(data)}.")
    print(f"Months: {len(summary)}.")
    print(f"Verified totals: units={source_totals[1]}, amount={source_totals[2]}.")
    print(f"Output: {OUTPUT.as_posix()}")


if __name__ == "__main__":
    main()
text
python monthly_summary_pandas.py

05想定出力と比較する

出力には month 順に並んだ3つの data rows が含まれるはずです。想定される CSV contents を以下に示します。

csv
month,orders,units,amount
2026-01,2,5,100
2026-02,3,7,175
2026-03,2,6,155

以下の想定 console output は、合成データとコードから手作業で導出したものです。実際に取得した execution log ではありません。

text
Source rows: 7.
Months: 3.
Verified totals: units=18, amount=430.
Output: outputs/monthly_summary_result/monthly_summary.csv

06結果を照合し、よくあるエラーを確認する

  1. 元データの rows を数えます: 7.
  2. 月次 orders を合計します: 2 + 3 + 2 = 7.
  3. 月次 units を合計します: 5 + 7 + 6 = 18.
  4. 月次 amount を合計します: 100 + 175 + 155 = 430.
  5. 出力に 2026-01, 2026-02, 2026-03 が正確に含まれていることを確認します。
  6. OUTPUT_DIR を変更せずにスクリプトを再実行します。既存の結果を置き換えるのではなく FileExistsError で停止するはずです。
症状確認すること
ModuleNotFoundError for pandaspython -m pip install pandas を使い、同じ Python environment に pandas をインストールします。
Missing required columns想定 schema を変更する前に CSV header を確認します。
Date parsing errorすべての order_date が YYYY-MM-DD を使用していることを確認します。
Numeric conversion errorunits と amount に text, currency symbols, separators が含まれていないか確認し、必要なら明示的な cleanup rule を定義します。
Duplicate order_id found重複 ID が accidental duplicate なのか、別の data model を表しているのか確認します。
FileExistsError以前の結果を確認し、上書きではなく新しい output folder を使用します。

grand totals が一致しても、すべての month が正しいことの証明にはなりません。2つの月次エラーが互いに相殺される可能性があるためです。重要な reports では、既知の source rows と month-level subtotals もいくつか確認してください。

07制限を理解する

この例では1行が1 order で、amount は整数と仮定しています。1つの order が複数の line-item rows にまたがる場合は、row count の代わりに order_id.nunique() などの rule を使用してください。実際の monetary data では、通常の floating-point arithmetic の代わりに fixed-point integer units または Decimal-based processing が必要になる場合もあります。

month は time-zone handling を行わず date から直接生成します。実データに複数の zones の timestamps が含まれる場合は、grouping 前に business time zone を定義してください。refunds, cancellations, filters, missing records にも明示的な business rules が必要です。arithmetic reconciliation だけでは、それらの rows を report に含めるべきか判断できません。

実行・検証の記録

2026-09-20 · 手作業で確認した例 · 対象: Python 3.12 · pandas 必須 · 未実行

  • 2026年1月、2月、3月にまたがる7件の source records を手作業で数えました。
  • source totals として 18 units と 430 amount を手作業で計算しました。
  • 1月を 2 orders, 5 units, 100 amount、2月を 3 orders, 7 units, 175 amount、3月を 2 orders, 6 units, 155 amount と手作業で計算しました。
  • 月次 totals を 7 orders, 18 units, 430 amount に手作業で reconcile しました。
  • required-column checks, duplicate IDs, explicit date parsing, numeric conversion, groupby aggregation, reconciliation, saved-file comparison, output collision protection についてスクリプトを確認しました。
  • 想定される CSV と console output を手作業で導出しました。
検証範囲の限界
  • この回答の作成者はコードを実行しておらず、pandas の動作や filesystem writes はここではテストしていません。
  • Decimal currency, time zones, refunds, cancellations, line-item order data, very large files はテストしていません。
  • 公式ドキュメントの URL は既知のドキュメント所在地に基づいて記載していますが、ライブでは確認していません。

サイト全体の執筆・検証方針

参考資料

説明と例は独自に作成しました。関連する動作や概念は、以下の公式資料で確認できます。