この記事でわかること

  • Excelの売上の明細の日付から、年月を取り出す方法(dt.to_period)
  • 月ごとの売上の合計と件数を、名前を付けて一度に出す方法(groupbyとagg)
  • 日付が文字のままのときのエラーと、日付として読めない行の扱い
  • 年月を文字に直さずに書き出したときに起きること

日付の入った売上の明細から、月ごとの合計を毎月手で集計していませんか。この記事では、pandasで日付を月にまとめ、月ごとの合計と件数を出してExcelに書き出す手順を紹介します。

拠り所にしたのは、pandas 3.1.0の公式の資料です。to_datetime・Series.dt.to_period・DataFrame.groupbyのAPIリファレンスと、ユーザーガイドの「Group by」を、2026年10月30日に原文で読みました。例の店舗と売上の数字は、この記事のために作ったものです。

先に結論: 日付に直して、年月の列でまとめる

手順は3段です。pd.to_datetimeで日付の列を日付に直し、dt.to_period("M")で年月の列を作り、その列でgroupbyして合計と件数を出します。 書き出す前に、年月は文字に直しておきます。

すること 使うもの
日付に直す pd.to_datetime
年月を取り出す dt.to_period("M")
月ごとにまとめる groupbyとagg

日付の列を日付に直す

Excelの日付の列は、日付のセルと、「2026/10/05」のような文字のセルが混ざっていることがあります。そのままでは、年月を取り出す.dtが使えません。

pd.to_datetimeは、初期の設定(errors="raise")では読めない値があると例外で止まり、errors="coerce"を付けると読めない値を空(NaT)にします。日付の直し方そのものは、PythonでExcelの日付を正しく読み込む手順で詳しく紹介しています。

年月を取り出す(dt.to_period)

Series.dt.to_periodは、日付を、決まった頻度の期間(Period)に変えます。頻度に"M"を渡すと、2026年9月3日も9月28日も「2026-09」という同じ月になります。資料の例でも、3月・5月・8月の月末の日付が、to_period("M")で「2000-03」「2000-05」「2000-08」になっています。

月ごとに合計と件数を出す(groupbyとagg)

DataFrame.groupbyは、ある列の値ごとに行を分けて計算し、結果をまとめます。年月の列で分ければ、月ごとの計算になります。

aggに「出力する列の名前=(元の列, 計算)」の形で渡すと、出力の列に名前を付けながら、複数の計算を一度に出せます。ユーザーガイドでは、この書き方を「名前付きの集計(named aggregation)」と呼んでいます。groupbyは初期の設定でグループの値を並べ替えるため、月は古い順に並びます。

手順: 売上の明細から月ごとの合計を書き出す

次のコードは、架空の売上の明細 sales.xlsx(日付・店舗・売上の3列)を読み込み、月ごとの合計と件数を monthly_total.xlsx に書き出します。sales.xlsx は書き換えません。自分の表に合わせるときは、ファイル名と列の名前を変えてください。

import pandas as pd

# 明細のファイルを読む(上書きはしない)
rows = pd.read_excel("sales.xlsx")

# 日付の列を日付に直す。読めない値は空(NaT)にして、その数を表示する
rows["日付"] = pd.to_datetime(rows["日付"], errors="coerce")
print("日付として読めなかった行:", rows["日付"].isna().sum())

# 日付から年月を取り出し、月ごとに合計と件数を出す
rows["年月"] = rows["日付"].dt.to_period("M")
monthly = rows.groupby("年月").agg(
    売上合計=("売上", "sum"),
    件数=("売上", "count"),
)

# 年月を「2026-09」のような文字にしてから書き出す
monthly.index = monthly.index.astype(str)
monthly.to_excel("monthly_total.xlsx")
print(monthly)

このコードの前に、pandasと、xlsxの読み書きに使うopenpyxlを入れておく必要があります。

実行した結果

架空の売上の明細(6行。日付のセル4つ、「2026/10/05」という文字の日付1つ、「不明」と書かれた行1つ)で動かしました。

  • 「日付として読めなかった行: 1」と表示されました。「不明」の行です
  • 月ごとの結果は、2026-09が売上合計20,000・件数2、2026-10が20,000・2、2026-11が9,000・1でした。文字だった「2026/10/05」の行も10月に入りました
  • 「不明」の行の売上3,000は、どの月にも入っていませんでした。明細の合計52,000に対し、月ごとの合計を足すと49,000です
  • 書き出したmonthly_total.xlsxの年月の列は、「2026-09」のような文字でした
  • 読み込んだsales.xlsxは、実行の前後でバイト単位で同じでした

Macの作業PCで試しました。Pythonは3.12.15、pandasは3.1.0、openpyxlは3.1.5です。Excelのアプリで開いたときの見た目は確かめていません。

自分で直すか、相談するかの判断

目安は、「月の区切りが、暦の月(1日から月末まで)でよいか」です。暦の月でよければ、この記事のコードで足ります。

20日締めのように会社ごとの締め日で区切る場合や、返品・取り消しの行の扱いに決まりがある場合は、集計の作りが変わります。経理の担当の人と区切り方や除く行を決めてから作り、作ったものは手で集計した月の数字と一度照らし合わせます。

つまずきやすいところ

  • 日付が文字のまま.dtを使う: 架空の明細でも、日付のセルと文字の日付が混ざった列に.dt.to_periodを使うと、「Can only use .dt accessor」で始まるエラーで止まりました。先にpd.to_datetimeで直します
  • 読めなかった行に気づかない: errors="coerce"で空になった行は、groupbyで黙って外れます。試したときも、明細の合計と月ごとの合計の差は「不明」の行の3,000でした。読めなかった行の数を表示し、合計を照らし合わせます
  • 年月を文字に直さずに書き出す: astype(str)を外して書き出すと、年月の列は「2026-09」ではなく、2026年9月1日のような月の初めの日付として入りました。月の表として見せるなら文字に直します
  • 店舗ごと・月ごとの表にしたい: 行に店舗、列に月を並べる表の作り方は、PythonでExcelのピボットテーブルのように集計する手順で紹介しています
  • 集計の結果を検算したい: 件数と合計を出して元のデータと照らし合わせる考え方は、PythonでCSVを集計する手順にまとめています
  • 出した月の合計から、前月比や累計も出したい: 店舗ごとに前月差・前月比・累計を計算する手順は、PythonでExcelの売上から前月比と累計を計算する手順で紹介しています

まとめ

  • 日付の列はpd.to_datetimeで日付に直し、読めなかった行の数を確かめます
  • dt.to_period("M")で年月の列を作り、groupbyとaggで月ごとの合計と件数を出します
  • 読めなかった行は集計から外れるので、明細の合計と月ごとの合計を照らし合わせます
  • 書き出す前に、年月をastype(str)で「2026-09」のような文字に直します