CHAPTER 6 ・ pandas を一歩進める / Lesson 25

日付で集計する(月次・前年比)

日付の列から月・年度を取り出し、月次推移や前月比を出す。時系列の集計の基本です。

目安 25分 ・ インストール不要

このレッスンが終わったら

「日付列を datetime 型にすると、dt で月や曜日を取り出せる。月ごとにまとめて、前月比も計算できる」と言える。

STEP 1

こんな場面、ありませんか?

「月ごとの売上の推移を出して、前の月と比べたい」。経理や営業事務で、毎月のように出てくる依頼です。Lesson 24 では、日付の先頭7文字を取り出して「月」の代わりにしました。しかし、それでは、曜日や年度、「7月から9月まで」のような期間の指定ができません。

sales_detail.csv の「日付」の列は、pandas から見ると、どんな型になっているでしょうか。月を取り出そうとして、確かめてみます。

日付の列から、月を取り出そうとするpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

型は object(文字)でした。Can only use .dt accessor with datetimelike values(日付の値にしか、.dt は使えません)と出て、止まっています。2025-04-01 は、人間には日付に見えますが、pandas にとっては、まだただの文字です。この回では、文字を日付に直して、月ごとの集計や前月比を出します。

STEP 2

日付型にして、月ごとに集計する

pd.to_datetime で、文字を日付型に変えます。そのあとは、.dt を付けて、年・月・曜日などを取り出せます。

日付型に変えて、年・月・曜日を取り出すpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

型が datetime64[ns](日付と時刻)に変わりました。曜日は数字で、月曜が 0、火曜が 1、…、日曜が 6 です。2025年4月1日は火曜日なので、1 になっています。

次は、月ごとの売上合計です。日付を「月」単位に直したものを、groupby に渡します。

月ごとの売上合計pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

4月から9月までの合計が並びました。to_period("M") は、日付を「2025年4月」のような月という期間に直します。「M」を「Y」にすると年ごと、「Q」にすると四半期ごとになります。

STEP 3

仕組みを読む

月ごとの集計の1行は、4つの部品でできています。

df.groupby(df["日付"].dt.to_period("M"))["金額"].sum() の部品
df.groupby(〜ごとにグループ分け
df["日付"]日付型の列
.dt日付用の道具箱を開ける
.to_period("M")月という期間に直す
)["金額"].sum()金額を合計する

日本語にすると

日付を月という期間に直して、同じ月どうしでグループに分け、金額を合計してください。

.dt は、文字の列に使った .str の、日付版です。日付型の列だけが使える道具が、並んでいます。

書き方取り出せるもの
.dt.year / .dt.month / .dt.day年 / 月 / 日(数字)
.dt.dayofweek曜日(月曜 0 〜 日曜 6)
.dt.to_period("M")月という期間("Y" は年、"Q" は四半期)

Lesson 03 で学んだ date 型の weekday() と同じく、月曜が 0 です。

STEP 4

変えて、試す

① 年度を求める。 会社の年度は、4月始まりのことが多いです。1〜3月は、前の年の年度になります。「月が4より小さければ、年から1を引く」と書きます。サンプルの売上は4〜9月だけなので、1〜3月を含む日付で確かめます。

4月始まりの年度pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

3月31日は2024年度、4月1日は2025年度、翌年1月15日は2025年度になりました。会社によって年度の始まりは違うので、自社のルールを確認してください。

② 前月比と、移動平均。 月ごとの合計に、前の月と比べた増減率(前月比)と、直近3か月の移動平均を並べます。月ごとの集計には、resample("MS") も使えます(MS は「月初ごと」)。

月次売上・前月比・3か月移動平均pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

pct_change() は、「今の値 ÷ 前の値 − 1」を出します。最初の月は比べる相手がないので NaN です。rolling(3).mean() は、直近3つの平均で、3つそろうまでは NaN です。9月は前月比 −65.8% と大きく落ちています。これは、9月分のデータが月の途中までしか入っていないのか、本当に売上が減ったのか、数字の意味を確かめるきっかけになります。

③ 期間で絞り込む。営業日数を数える。 between で、期間内の行だけを取り出せます。営業日の数は bdate_range で数えます。祝日を除くときは、freq="C" と、holidays に祝日の一覧(holidays.csv)を渡します。

期間の絞り込みと、9月の営業日数pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

between は、両端の日付を含みます。9月は、平日が22日ありますが、9月15日(敬老の日)と23日(秋分の日)を除いて、営業日は20日です。

④ 欠けている月を、0で補う。 取引のなかった月は、groupby の結果に出てきません。グラフや前月比がずれる原因になります。アオイ電機(C001)で確かめます。

取引のない月を 0 で補うpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

4月に取引がなかったので、はじめは5か月分しかありません。reindex で、4月から9月までという範囲を自分で決めて、ないところを 0 で埋めました。(resample も、途中で欠けた月は 0 で補いますが、最初や最後の欠けは補えません)

⑤ 日付の書き方が混ざっているとき。 Lesson 22 の messy.csv には、2025/10/01 と 2025-10-03 が混ざっていました。

書き方が混ざった日付を変換するpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

time data "2025-10-03" doesn't match format "%Y/%m/%d" と出ます。最初の日付の書き方に、全部を合わせようとして、合わないものが見つかって止まりました。先に df["日付"].str.replace("/", "-") で書き方をそろえるか、format="mixed" を付けます。うまく変換できない日付だけを NaT(日付版の欠損値)にしたいときは、errors="coerce" を付けます(Lesson 22 と同じ考え方です)。

STEP 5

自分で書く

お題① 月ごとの売上合計(数量×単価)を求め、売上がいちばん大きい月を表示してください。(ヒント:idxmax() は、最大の値がある行の名前を返します)

お題①:売上がいちばん大きい月pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

df["日付"] = pd.to_datetime(df["日付"]) のあと、df.groupby(df["日付"].dt.to_period("M"))["金額"].sum() で月ごとの合計を出し、.idxmax() を付けます。

答えの例を見る
import pandas as pd

df = pd.read_csv("sales_detail.csv")
df["金額"] = df["数量"] * df["単価"]
df["日付"] = pd.to_datetime(df["日付"])

monthly = df.groupby(df["日付"].dt.to_period("M"))["金額"].sum()
print(monthly.idxmax())

お題② 月ごとの売上合計から、8月の前月比(%)を、小数第1位まで表示してください。

お題②:8月の前月比pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

rate = monthly.pct_change() * 100 で前月比(%)が全部の月で出ます。8月の値は rate["2025-08"] で取り出せます。round(..., 1) で小数第1位にします。

答えの例を見る
import pandas as pd

df = pd.read_csv("sales_detail.csv")
df["日付"] = pd.to_datetime(df["日付"])
df["金額"] = df["数量"] * df["単価"]
monthly = df.groupby(df["日付"].dt.to_period("M"))["金額"].sum()

rate = monthly.pct_change() * 100
print(round(rate["2025-08"], 1))

お題③ 2025年7月1日から9月30日までの売上(数量×単価)の合計を、between を使って表示してください。

お題③:7〜9月の売上合計pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

df["日付"].between("2025-07-01", "2025-09-30") で、期間内の行が True になります。df[...] で取り出して、金額の合計(.sum())を求めます。

答えの例を見る
import pandas as pd

df = pd.read_csv("sales_detail.csv")
df["日付"] = pd.to_datetime(df["日付"])
df["金額"] = df["数量"] * df["単価"]

q = df[df["日付"].between("2025-07-01", "2025-09-30")]
print(q["金額"].sum())
STEP 6

仕事ならどう使う?

理解チェックdf["日付"].dt.dayofweek の結果が 0 のとき、何曜日でしょう?

理解チェック4月始まりの年度で、2026年2月10日は、何年度でしょう?