月次レポートv2
このレッスンでできるようになること
その月の合計、前の月との差、カテゴリ別の内訳をまとめて返す月次レポートを、中級の道具だけで組み立てられるようになります。
レポートは3つの部品でできています
入門で作った家計簿のレポートは、合計を出して終わりでした。v2では次の3つを出します。
- その月の合計金額
- 前の月と比べていくら増えたか減ったか
- カテゴリ別の内訳を、金額の大きい順に
どれも第3章と第4章でやったことの組み合わせです。新しい文法は出てきません。
その月だけを取り出す
日付を "2026-08-01" の形で持っておくと、月での絞り込みは文字列の先頭一致で書けます。
this_month = [e for e in expenses if e["date"].startswith("2026-08")]startswith は「その文字列で始まるか」を返すメソッドです。第3章の条件付き内包表記に、そのまま条件として置けます。
前の月を求める
"2026-08" の前は "2026-07" です。ところが "2026-01" の前は "2025-12" で、年をまたぎます。ここは素直に数値に直して考えます。
def previous_month(month):
year, m = month.split("-")
year, m = int(year), int(m)
if m == 1:
return f"{year - 1}-12"
return f"{year}-{m - 1:02d}"{m - 1:02d} は「2桁の整数、足りない桁は0で埋める」という書き方です。7 が 07 になります。これを忘れると 2026-7 になり、startswith での比較が合わなくなります。
増減は符号を自分で付けます
差がプラスかマイナスかは、見た人にすぐ分かる形で出したいところです。
diff = total - prev_total
sign = "+" if diff >= 0 else "-"
line = f"前月比 {sign}{abs(diff)}円"第3章の if-else 式が、こういう小さな分岐で効いてきます。abs は絶対値を返す組み込み関数で、これを挟むことで - が二重に付くのを防いでいます。
内訳は Counter と sorted で
カテゴリ別の集計は第2章の Counter、並べ替えは第4章の sorted と key です。金額の大きい順にしたいので、キーにマイナスを付けます。
for category, amount in sorted(totals.items(), key=lambda kv: (-kv[1], kv[0])):
print(f"{category} {amount}円")(-kv[1], kv[0]) はタプルのキーです。まず金額の降順で並び、金額が同じときはカテゴリ名の昇順で並びます。同額のときの順番を決めておかないと、実行するたびに並びが変わる可能性が残ります。レポートのように人が読むものは、いつ動かしても同じ順で出ることが大事です。
戻り値は1行1要素の文字列のリストにしておきます。こうしておくと、画面に出すのもファイルに書くのも呼ぶ側の自由になります。
要件
- previous_month(month) を書く。2026-08 なら 2026-07、2026-01 なら 2025-12 を返す
- 月は必ず2桁にする。2026-7 のような形にしない
- monthly_report(expenses, month) は行の文字列を並べたリストを返す
- 1行目は 2026-08 の合計 6130円 の形にする
- 2行目は 前月比 +1050円 の形にする。減っているときは 前月比 -300円 とする
- 3行目以降はその月のカテゴリ別合計を、金額の降順、同額ならカテゴリ名の昇順で 食費 4050円 の形で並べる
- 対象の月に支出が1件も無いときは、合計0円と前月比の2行だけを返す
入出力例
monthly_report([{"amount":2900,"category":"食費","date":"2026-07-03","name":"スーパー"},{"amount":1200,"category":"交通費","date":"2026-07-10","name":"電車代"},{"amount":980,"category":"食費","date":"2026-07-22","name":"ランチ"},{"amount":3200,"category":"食費","date":"2026-08-01","name":"スーパー"},{"amount":480,"category":"交通費","date":"2026-08-02","name":"電車代"},{"amount":850,"category":"食費","date":"2026-08-03","name":"コンビニ"},{"amount":1600,"category":"教養","date":"2026-08-05","name":"技術書"}], "2026-08") → ["2026-08 の合計 6130円","前月比 +1050円","食費 4050円","教養 1600円","交通費 480円"]
monthly_report([{"amount":5000,"category":"交際費","date":"2025-12-20","name":"忘年会"},{"amount":300,"category":"交際費","date":"2026-01-01","name":"初詣"},{"amount":4400,"category":"食費","date":"2026-01-06","name":"スーパー"}], "2026-01") → ["2026-01 の合計 4700円","前月比 -300円","食費 4400円","交際費 300円"]
monthly_report([{"amount":2900,"category":"食費","date":"2026-07-03","name":"スーパー"},{"amount":1200,"category":"交通費","date":"2026-07-10","name":"電車代"},{"amount":980,"category":"食費","date":"2026-07-22","name":"ランチ"},{"amount":3200,"category":"食費","date":"2026-08-01","name":"スーパー"},{"amount":480,"category":"交通費","date":"2026-08-02","name":"電車代"},{"amount":850,"category":"食費","date":"2026-08-03","name":"コンビニ"},{"amount":1600,"category":"教養","date":"2026-08-05","name":"技術書"}], "2026-09") → ["2026-09 の合計 0円","前月比 -6130円"]