課題 ── 依頼台帳から何を知りたいか
例の課題は、文書確認の依頼台帳(Excel、40 行、架空)から、「依頼部署と種別ごとに、依頼から完了までの日数と差戻しの割合を、月ごとに知りたい」というものです。台帳の列は、依頼ID・文書名・種別・依頼部署・依頼日・担当者・状態・指摘件数・完了日です。
この課題を、分解・抽象化・パターン認識・アルゴリズム設計・検証の 5 つの考え方で順に解きます。
1. 分解 ── 答えられる小さな問いに分ける
- 1 件ごとに「完了までの日数」を出す
- 1 件ごとに「差戻しか」を決める
- 月・部署・種別の組にまとめる
- まとめた数を表にして書き出す
大きな問いのままでは、どこから手を付けるかも、答えが正しいかも分かりません。「1 件について答えられる問い」まで分けると、1 行ずつ手で確かめられます。
2. 抽象化 ── 要らない情報を捨て、言葉を定義する
| 列 | 使うか | 理由 |
|---|---|---|
| 依頼日・完了日 | 使う | 日数の計算に要る |
| 状態 | 使う | 差戻しか、完了しているかを決める |
| 依頼部署・種別 | 使う | まとめる単位 |
| 依頼ID | 使う | 1 件を見分け、件数を数える |
| 文書名・担当者 | 使わない | 問いに関係しない。個人に関わる情報は、要らなければ持ち出さない |
| 指摘件数 | 使わない(今回) | 問いに無い |
言葉の定義: 「完了までの日数」は、完了日 − 依頼日(状態が 完了 か 差戻し の行だけ)。「差戻しの割合」は、差戻しの件数 ÷(完了 + 差戻しの件数)。受付・確認中の行は、日数にも割合にも入れません。定義を先に書くと、Copilot に頼むときも、結果を確かめるときも、同じ物差しを使えます。
3. パターン認識 ── 同じ形の計算を見つける
「部署ごと」「種別ごと」「月ごと」は、どれも「行をある基準で組に分け、組ごとに同じ計算をして、結果を 1 つの表にまとめる」という同じ形です。pandas の文書は、この形を、基準で組に分ける・組ごとに関数を当てる・結果をまとめる、の 3 段として説明しています(groupby)。
月ごとの組は、日付を月の単位に変えれば同じ形になります。pandas の文書によると、Series.dt.to_period は日付を指定した頻度の期間に変えます(月なら "M")。
4. アルゴリズム設計 ── 順番を決めて書く
- 台帳を読む(pandas.read_excel)
- 状態が 完了・差戻し の行だけを残す
- 日数の列を作る(完了日 − 依頼日)
- 差戻しの印の列を作る
- 依頼月の列を作る(依頼日を月の期間に)
- 部署・種別・月の組にし、件数・日数の中央値・差戻しの割合を出す
- 表を新しいブックに書き出す
「残す → 列を足す → 組にする → まとめる」の順にすると、各段の途中の表を見て確かめられます。
集計のコード(見本の台帳で実行)
"""Tally a request ledger: days to finish and return rate by department, type and month."""
import pandas as pd
ledger = pd.read_excel("samples/request_ledger.xlsx", sheet_name="依頼台帳")
done = ledger[ledger["状態"].isin(["完了", "差戻し"])].copy()
done["日数"] = (pd.to_datetime(done["完了日"]) - pd.to_datetime(done["依頼日"])).dt.days
done["差戻し"] = done["状態"] == "差戻し"
done["依頼月"] = pd.to_datetime(done["依頼日"]).dt.to_period("M")
summary = (done.groupby(["依頼部署", "種別", "依頼月"])
.agg(件数=("依頼ID", "count"), 日数の中央値=("日数", "median"), 差戻しの割合=("差戻し", "mean"))
.reset_index())
summary["依頼月"] = summary["依頼月"].astype(str)
summary.to_excel("out_summary.xlsx", index=False)
print("rows in ledger:", len(ledger), "/ rows used:", len(done), "/ groups:", len(summary))
print(summary.head(8).to_string(index=False))実行結果(2026-09-12)
rows in ledger: 40 / rows used: 25 / groups: 15
依頼部署 種別 依頼月 件数 日数の中央値 差戻しの割合
人事部 Web 記事 2026-07 1 6.0 0.0
人事部 プレスリリース 2026-07 1 12.0 1.0
人事部 提案書 2026-07 1 5.0 0.0
人事部 提案書 2026-08 1 3.0 1.0
人事部 社内通知 2026-07 1 12.0 0.0
人事部 社内通知 2026-08 3 14.0 0.0
営業部 Web 記事 2026-07 1 14.0 1.0
営業部 契約書 2026-08 1 5.0 0.05. 検証 ── 別の道で同じ答えになるか確かめる
- 組ごとの件数の合計が、残した行の数と一致するか
- 日数に負の値が無いか(完了日が依頼日より前の行が無いか)
- いちばん大きな組を選び、groupby を使わずに条件で絞り込んで数えた数と一致するか
- 差戻しの割合が 0 以上 1 以下か
検証のコード(assert が 1 つでも外れると止まる)
"""Check the ledger tally by a second route: invariants and one group counted by plain filtering."""
import pandas as pd
ledger = pd.read_excel("samples/request_ledger.xlsx", sheet_name="依頼台帳")
done = ledger[ledger["状態"].isin(["完了", "差戻し"])].copy()
done["日数"] = (pd.to_datetime(done["完了日"]) - pd.to_datetime(done["依頼日"])).dt.days
by_group = done.groupby(["依頼部署", "種別"]).size()
assert by_group.sum() == len(done), "group counts must add up to the rows used"
assert (done["日数"] >= 0).all(), "a finish date is earlier than its request date"
dept, kind = by_group.idxmax() # the largest group
manual = ((ledger["依頼部署"] == dept) & (ledger["種別"] == kind) & ledger["状態"].isin(["完了", "差戻し"])).sum()
assert manual == by_group[(dept, kind)], "groupby and plain filtering disagree"
rate = (done["状態"] == "差戻し").mean()
assert 0 <= rate <= 1, "a rate must be between 0 and 1"
print("largest group checked by plain filtering:", dept, kind, int(manual), "rows")
print("all checks passed")実行結果(2026-09-12)
largest group checked by plain filtering: 広報部 プレスリリース 5 rows
all checks passed検証は、集計と同じ道(groupby)で数え直すのではなく、別の道(条件での絞り込み)で数えます。同じ道で数え直すと、同じ誤りを 2 回くり返すだけになりうるからです。
6. Copilot に頼むとき ── 分解と定義をそのまま指示にする
pandas で、Excel の依頼台帳(シート名 依頼台帳。列: 依頼ID, 文書名, 種別, 依頼部署, 依頼日, 担当者, 状態, 指摘件数, 完了日)を集計してください。 定義: 完了までの日数=完了日−依頼日(状態が 完了 か 差戻し の行だけ)。差戻しの割合=差戻しの件数÷(完了+差戻しの件数)。 依頼部署・種別・依頼月(依頼日の月)ごとに、件数・日数の中央値・差戻しの割合を出し、新しいブックに書き出してください。文書名と担当者の列は使わないでください。 あわせて、件数の合計が使った行数と一致すること、日数に負の値が無いことを確かめる assert を書いてください。
「差戻し率」とだけ書くと分母が決まらないため、受付や確認中の行を分母に入れたコードが返っても、指示に照らしては誤りと言えません。だから定義を指示に書きます。
まとめ ── 5 つの考え方の使いどころ
| 考え方 | 問い | 例での答え |
|---|---|---|
| 分解 | 1 件について答えられる問いは何か | 日数、差戻しかどうか |
| 抽象化 | 何を捨て、言葉をどう定義するか | 文書名・担当者を捨てる。日数と割合を式で定義する |
| パターン認識 | 同じ形の計算はどこか | 部署・種別・月の組ごとの集計(groupby) |
| アルゴリズム設計 | どの順で処理するか | 残す → 列を足す → 組にする → まとめる |
| 検証 | 別の道で同じ答えになるか | 合計の一致・負の値が無い・1 組を絞り込みで数える |
同じ考え方を、1 つの作業を最初から最後まで通して使う例は 着想から運用までの通し手順 にあります。