Maverick Spend: find the purchases that bypassed the contracts
The data in this project is synthetic — generated locally by
data/make_data.py, with no external source. That is deliberate: you can only score a compliance screen if you know which purchases truly broke the rules, and labelled procurement data is exactly what companies never share. No signup, no downloads, no GPU.
Before you start
New to procurement/spend analytics? Start here.
Companies negotiate contracts: for a category of spend (say “IT Hardware”), a few preferred vendors agree to a negotiated price. Buying inside that contract is compliant. Maverick spend is buying around it — the same category, but from a non-preferred vendor — usually at a worse price. It’s one of the biggest, quietest sources of wasted money in a large organization, because each purchase looks fine on its own; only in aggregate does the leak show.
The catch that trips up a first attempt: not every category has a contract. Some spend is spot (one-off consulting, legal, travel) with no preferred vendor at all — so buying from “an unlisted vendor” there is perfectly fine. A screen that flags every unlisted vendor drowns you in false positives from spot categories. Conditioning on “is this category even under contract?” is the whole skill.
A tiny example:
category contracted? preferred vendors this PO's vendor verdictIT Hardware yes {Dell, HP} Lenovo MAVERICKConsulting no (spot) — McKinsey fine (no contract)What you’ll learn: flag maverick spend correctly (only within contracted categories), quantify the leakage (what it cost above the negotiated price), and read the tail spend (the long list of tiny suppliers). Section 3 lists exactly what you’ll be able to do.
New to Python too? See Start Here — it starts from zero, and shows how to run this in your browser with nothing to install.
1. The Brief
You’re handed a year of purchase orders and the contracts that are supposed to govern them, and asked: how much of our spend went around the contracts, what did that cost us, and where should we consolidate? You’ll build a screen that flags off-contract purchases correctly — without drowning in false positives — and put a dollar figure on the leak.
Every procurement and finance team lives this. A CFO is told “we’re 90% on-contract” and the real number is 70% once you strip out the categories that were never under contract to begin with — or it’s worse than reported because the screen only checked vendors, not prices, and missed the preferred suppliers quietly charging above the negotiated rate. The number decides where a sourcing team spends the next quarter; getting the rule right is the job.
2. Difficulty & time
Difficulty 4 / 10. One tool (pandas), one concept (policy conformance), but with a genuine
analytical trap — the spot-vs-contracted distinction — that a naive screen gets wrong.
Calibrated against the ledger (G10): a peer of newsvendor-stocking and feature-scaling
(both 4) — a single clean mechanic with one judgment call that separates a right answer from a
plausible-wrong one. It sits above data-contracts and benford-fraud (both 3), which
apply a fixed rule set with no conditional subtlety, and below accuracy-is-lying (5),
which runs a full modelling-and-evaluation pipeline. The Part B transfer (a second,
independent leak rule) is what keeps it at 4 rather than 3.
Time is separate from difficulty: Part A is about an hour of reading and running; Part B is 2–3 hours of your own work. The scripts run in under a second — the time is comprehension.
3. What you’ll be able to do after
- Flag maverick spend correctly — off-contract purchases within contracted categories — and explain why flagging every unlisted vendor (including spot categories) is wrong.
- Quantify leakage — the dollars maverick purchases cost above the negotiated price, i.e. what routing them through the contract would recover.
- Read tail spend — rank vendors by spend, find the long tail of tiny suppliers, and size the consolidation opportunity with a Pareto view.
- Screen for a second leak — a preferred vendor charging above the negotiated price (a price breach) — and combine rules into one per-PO compliance decision.
The finished result
By the end of Part A you’ll have screened 900 purchase orders and separated the real leak from the false alarms the naive rule raises:
naive flag (any unlisted vendor): 282 POs ~$1.5M <- over-counts: includes spot spendcorrect maverick flag (contracted): 128 POs ~$0.45M <- the real off-contract spendrecoverable leakage vs. negotiated: $79,344tail spend: 40 vendors; the top 8 (20%) are ~50% of spendThe 154-PO gap between the two flags is the whole skill — it’s the spot spend the naive rule wrongly blames — and the honest read is 128 off-contract POs with $79k of premium you could win back.
4. Prereqs & time box
You can write a for loop and a function, and you’ve used a pandas DataFrame (groupby,
boolean masks). No procurement background needed — the primer above covers it.
Where to write and run code
Two ways to run this project — pick either:
- In your browser (no install). If the project page shows a ▶ Run button, click it to run Part A right here — nothing to set up. Part A uses only pandas and numpy, which run in the browser.
- On your computer. The one-time setup — installing Python, downloading the code from the
course site (no GitHub, no
git), opening a terminal, creating a virtual environment, installing the requirements, and running Part A step by step — is covered once in Start Here. Come back here oncepython -c "import pandas"runs without error. Part B (writing your own solution) needs this local setup.
Time box
- Part A — Guided Build: 60–90 minutes. Hard cap.
- Part B — Your Turn: 2–4 hours. Hard cap.
5. Setup & data
The data is synthetic and generated locally — see the banner above and SOURCES.md for
full provenance. One command creates everything:
python data/make_data.pyThat writes four CSVs into data/ (git-ignored; regenerate anytime, byte-for-byte identical
because the seed is fixed):
- Part A —
pos_a.csv(900 purchase orders) andcontracts_a.csv(10 contracted categories). - Part B —
pos_b.csv(2,400 purchase orders) andcontracts_b.csv.
Each PO row is po_id, date, category, vendor, qty, unit_price, amount. Each contract row is
category, preferred_vendors (pipe-separated), negotiated_unit_price. A category not in
the contracts file is spot spend — no contract to break. Whether each PO is compliant is
not in the files; the grader recovers it from the seed.
6. Part A — Guided Build
You’ll screen the 900-PO pos_a.csv. Run the whole thing, or (better) one # %% cell at a
time — see Start Here:
python part_a/screen_spend.pyPrefer the browser? If this page has a ▶ Run button, click it instead of the command above — Part A runs in your browser with nothing to install. The STEP output and every checkpoint below are identical either way.
The full, commented script is in part_a/screen_spend.py. The spine:
Step 1 — The lay of the land. Total spend and the top categories.
Checkpoint: 900 POs, ~$3.7M total; 10 contracted + 6 spot categories.
Step 2 — The naive flag, and why it over-counts. Flag any PO whose vendor isn’t preferred for its category — applied to every PO, including spot categories that have no preferred list.
Checkpoint: 282 POs flagged (~$1.5M). Far too many — it flags every spot purchase, which had no contract to bypass.
Step 3 — The correct flag. Maverick = the category is contracted and the vendor isn’t preferred for that category.
Checkpoint: 128 true maverick POs (~$0.45M). The ~154-PO gap from Step 2 is the spot purchases the naive rule wrongly flagged.
Step 4 — Leakage. For the maverick POs, the overspend vs. the negotiated price.
Checkpoint: ~$79k recoverable — the premium paid for going off-contract.
Step 5 — Tail spend. Rank vendors by spend; find the Pareto split and the long tail.
Checkpoint: 40 vendors; the top 20% (8 vendors) are ~50% of spend; 21 vendors make up the last 20% — the consolidation targets. Writes
part_a/screened_a.csv.
Why this and not that
- Why condition on “is the category contracted,” not just “is the vendor preferred.” A preferred-vendor list only means something where a contract exists. Applying it to spot spend manufactures false positives and destroys the credibility of the number — the naive Step-2 count is nearly double the real one.
- Why measure leakage in dollars, not just count POs. “128 maverick POs” doesn’t move a CFO; “$79k we can win back by routing this through the contract” does. The dollar figure is the deliverable.
- Why look at the tail separately. Maverick spend is about breaking existing contracts;
tail spend is about the vendors you should put under contract. Different problem, same
dataset — and both come out of one
groupby.
Functions you’ll meet (and where to read more)
| Function | What it does here | Docs |
|---|---|---|
df.groupby(col)[c].sum() | Spend by category / vendor | groupby |
boolean mask df[cond] | Select the flagged rows | boolean indexing |
s.isin(values) | Test vendor membership in a preferred set | isin |
s.map(mapping) | Attach the negotiated price by category | map |
s.sort_values() / s.cumsum() | Rank vendors and build the Pareto curve | cumsum |
s.clip(lower=0) | Keep only positive overspend | clip |
If something looks off
- The “correct” flag finds FEWER POs than the naive one (128 vs 282) — did I break it? No — that’s the whole point. The naive rule flags every unlisted vendor, including the spot categories that never had a contract to break; conditioning on “is this category even contracted?” removes those false alarms, so the honest number is smaller, not bigger. A screen that flags more isn’t catching more leakage here — it’s crying wolf on spot spend.
- A checkpoint number doesn’t match. The data is seeded, so the numbers are exact. Re-run
python data/make_data.py(you may have skipped or edited it), then re-run Part A. - Setup problems (Python not found,
pip,FileNotFoundError, re-activating the venv): see the troubleshooting list in Start Here.
7. Part B — Your Turn
Part B is the optional “your turn” half — a guided fill-in-the-blank, not a blank page. You get a working starter (
part_b/starter.py) that already handles the easy version; your job is to finish the# TODOit marks out. More open than Part A’s step-by-step, but you are never staring at an empty file. If you finished Part A and want to prove the skill, this is where it happens. New to Python? It’s completely fine to stop after Part A and come back later.
Same core skill, bigger file, and a second leak to catch. Screen the 2,400-PO pos_b.csv
against contracts_b.csv and flag every non-compliant PO. The starter (part_b/starter.py)
flags only maverick spend and scores ~0.68 — below the floor.
Here’s the transfer: money leaks two ways, not one. A PO is non-compliant if it’s maverick
(contracted category, non-preferred vendor) or a price breach — a preferred vendor
charging more than the negotiated price (here, above negotiated × 1.15). A screen that only
checks vendors misses every price breach.
Acceptance criteria (checkable)
Your solution writes into part_b/out/:
flagged.csv— one columnpo_id, every PO you flag as non-compliant.report.json— with keyspos,flagged,achieved_metric(you can leaveachieved_metricnull; the grader scores you).
Then:
python part_b/starter.pypytest part_b/test_solution.py -qIf
pytestreports everything asskipped, it just means the output files don’t exist yet — runpython part_b/starter.pyfirst (once your solution fills in the TODOs), then re-run.
You’re done when the tests pass: your flags reach F1 ≥ 0.90 against the true non-compliant set (the reference reaches 1.00). Maverick-only scores ~0.68 and fails — the price-breach rule is what clears the floor.
Constraints — what must be true of your solution, not how to get there
- Spot categories (absent from the contracts file) are never non-compliant — don’t flag them.
- Catch both leaks: off-contract vendor and over-negotiated price from an on-contract vendor.
- Your counts must be defensible: if
flaggedis near 900+ you’re over-flagging (probably spot categories); if it’s near the maverick-only ~480 you’re missing the price breaches.
Hints
Hint 1 — start from the contract table
Build two lookups from contracts_b.csv: category → set of preferred vendors, and category →
negotiated price. A category missing from those lookups is spot — skip it entirely.
Hint 2 — two independent rules
For a PO in a contracted category: it’s maverick if the vendor isn’t preferred; otherwise it’s
a price breach if unit_price > negotiated_price * 1.15. Either one makes it non-compliant.
Hint 3 — check your recall (still not the code)
Compare your flagged count to the starter’s. If they’re identical, you haven’t added the price-breach rule — those are the preferred-vendor rows the maverick check waves through.
8. Self-check
You don’t need the answer key. You’re done when all of these hold:
- No spot-category PO is flagged.
pytestreports F1 ≥ 0.90 (aim for 1.00 — both rules are deterministic once you get them right).- Your flagged count is clearly above maverick-only (you added the price breaches) and clearly below the naive “everything unlisted” count.
- Re-running from
python data/make_data.pyreproduces the same numbers — the data is seeded.
And the tell-tale: if your flagged count exactly equals the starter’s, you’re still missing an entire class of leak (the over-charging preferred vendors).
9. Stretch
- Dollarize it. Report total leakage (overspend vs. negotiated for every non-compliant PO, not just maverick ones) so the finding lands as a recoverable figure, not a row count.
- Rank the offenders. Which categories and which vendors drive the most non-compliant spend? That list is the sourcing team’s Monday-morning to-do.
- (Genuinely hard) Split-PO structuring. Some buyers split one purchase into several POs each just under an approval threshold. Group by (vendor, category, near-date) and flag clusters that individually clear the threshold but together exceed it — a leak neither the maverick nor the price rule catches.
10. Ship it
Put this in a portfolio repo and it counts. In that repo’s README:
- State the finding in one sentence a CFO gets: “$0.45M of spend went off-contract and $79k of it was pure premium — here’s the screen that found it.”
- Show the before/after: the naive flag count next to the corrected one, and the reconciliation that proves the difference is spot spend, not real maverick spend.
- Include the two-rule compliance function and one paragraph on the spot-vs-contracted trap.
That paragraph is the part that signals you’ve done spend analysis, not just a
groupby.
Keep make_data.py and the tests in the repo so anyone can reproduce your numbers from zero.
11. Sources
Rendered from SOURCES.md.
| Field | Value |
|---|---|
| Dataset | Synthetic procurement spend — POs + contracts (Part A 900, Part B 2,400 POs) |
| Type | Synthetic |
| Generated by | data/make_data.py (this repo), seeds 20260718 (A) and 20260719 (B) |
| Source URL | Not applicable — generated, not fetched |
| License | Not applicable — synthetic, no third-party rights |
| Access / generation date | 2026-07-18 |
| Redistribution | Permitted — synthetic; output regenerated from the seeded script, never committed |
| robots.txt checked | Not applicable — no scraping |
| ToS URL | Not applicable — no external source |
Synthetic data teaches the mechanic; it does not prove a finding about the real world. The compliance patterns were injected on purpose — recovering them demonstrates the technique, not a claim about any real company’s spend.
Next up
Finished this one? Continue the Data Analytics track:
Pipeline Forecast: weight the deals you have by the deals that actually close → · Browse all courses