The last mile is the whole point
Seven sessions in, you can load, clean, join, group, and chart 124,000 real job postings. That's real skill - but a notebook full of true tables is not an analysis anyone will read. The last mile is turning that work into findings a human wants to act on, a model that makes a testable prediction, and a package you can hand to a stranger. That last mile is where analysts become analysts people trust. Today you walk it end to end and finish with something you can put your name on.
From numbers to findings 4 min live
Output is what the computer prints. A finding is what a human should do about it. The gap between the two is the single most valuable thing you'll learn today - and it's not code.
LiveWhat makes a finding a finding2 min▶
A finding has three parts, and a wall of tables has none of them. Every headline you write today passes this test:
- A subject - who or what is this about? "Senior data engineers", not "the data".
- A number - quantified, with the sample size. "$142k median (n=1,830)", not "high pay".
- A so-what - why should the reader care, and what's the caveat? "...but that's the 30% who disclose salary."
Then structure the report the way people actually read: lead with the headline, one chart per point, quantify everything, and name the caveat out loud. The biggest caveat in this dataset is unavoidable - roughly 70% of postings have no salary, so every pay finding is about the salaried subset. Say so. Honesty is what makes the whole thing trustworthy.
"Median data-analyst salary is $X (n=1,204 salaried postings)" beats a wall of groupby tables every single time. The table is your evidence; the finding is what you actually put in front of a human. On LinkedIn, the finding is the post - the table is the reply when someone asks "source?".
Self-studyThe five findings, sketched2 min read▶
You already computed most of these across Sessions 5-7. The job now is to phrase them as findings, not describe them as outputs:
| Raw output you have | Phrased as a finding |
|---|---|
| groupby role -> median salary | "Data engineers out-earn analysts by ~35% at the median (salaried subset)" |
| value_counts on location | "Three metros hold a quarter of all senior postings" |
| groupby experience_level | "Each step up the level ladder adds roughly $Xk to median pay" |
| skill counts (skills table) | "SQL and Python appear in over half of analyst postings" |
| salary disclosure rate | "Only ~30% of postings disclose pay - the honest caveat on all of the above" |
A first predictive model 6 min live
Describing is "what happened". Predicting is "given a new role, what would it pay?". You already have everything you need to build an honest first model - and the framing matters more than the algorithm.
LiveSupervised learning, in plain words3 min▶
Supervised learning is just this: you have example rows where you know the answer, and you want to predict the answer for new rows. Split your table into two:
- Features (X) - the inputs you'd know about a new posting: role bucket, location, experience level.
- Target (y) - the thing you want to predict:
salary_year, the column you built back in Session 4.
Then you hold out some rows (the test set) that the model never sees while learning, so you can honestly ask "how would this do on a posting it's never met?". You encode the categorical columns - a model can't read the word "Senior", so get_dummies turns each category into 0/1 columns (one-hot encoding). Finally you fit a LinearRegression: it learns a weight per feature - roughly "how many dollars does being Senior add?".
A linear model on role + location + level won't nail any single salary - real pay depends on company, negotiation, luck. But it will tell you the shape: which factors move pay most, and by how much. That's a genuinely useful thing to say on LinkedIn - "location adds more to pay than one level of seniority, in this dataset" is a finding a model earned.
Self-studyThe honest caveats2 min read▶
State these out loud in the report. They're not weaknesses to hide - naming them is what separates an analyst from a hype account:
- Trained on the salaried 30%. The model only knows postings that disclosed pay. Those skew larger and more formal - it may not generalize to the silent 70%.
- Correlation, not cause. The model finds patterns, not mechanisms. "Senior adds $Xk" describes this data; it doesn't promise you a raise.
- Linear is a simplification. Real pay isn't a straight line. A modest fit is expected and fine - Part 3 shows how to say that honestly.
Validate honestly, then package 5 min live
A model that looks brilliant on its training data is telling you nothing - it may have just memorized. Judging it fairly, in units a human understands, then wrapping it so anyone can re-run it: that's the professional finish.
LiveNever judge a model on training data3 min▶
This is the single most important idea in Part 3. If you score the model on the same rows it learned from, it flatters itself - it can memorize its way to a great score and fall apart on anything new. That's overfitting, and the held-out test set is how you catch it. Score only on data the model has never seen. Two numbers say it plainly:
- MAE (mean absolute error) - the average dollars your prediction is off, in the target's real units. "MAE = $18k" means predictions miss by about $18,000 on average. Human-readable, no interpretation needed.
- R2 - the share of the variance in salary the model explains, from 0 to 1. R2 = 0.35 means it explains about a third of the spread; the rest is factors you didn't feed it.
And always compare to a baseline: what if you just predicted the mean salary for everyone? If your model can't beat that dumb guess, it isn't earning its keep. Beating the baseline - even modestly - is the honest bar.
LivePackage it so a stranger can run it2 min▶
Three moves turn a working notebook into a shareable artifact:
- A clean notebook, top to bottom. Restart & Run All must produce every table and chart with no manual steps and no errors. If it only runs in the order you happened to click, it isn't reproducible.
- An exported report.
jupyter nbconvert --to htmlfreezes the notebook into a file you can email or host - no Python required to read it. PDF works too. - A LinkedIn-ready summary. Hook, three findings, one chart, one honest caveat, soft CTA. No hype, no "🚨 SHOCKING". The restraint is the credibility.
This is exactly Phoebe's KOL "ship it" move: finish the thing, credit yourself ("Analysis by [you]"), and end with a question that invites discussion rather than a demand for likes. A post that says "here's what I found, here's my caveat, what am I missing?" starts conversations - and conversations are what build a following. The finished report is the proof; the caveat is the invitation.
Assemble the five findings ★ 8 min · everyone writes
Open your saved summary frames from Sessions 5-7 (the groupby results you exported). If you saved them as CSVs, reload them; if they're in the notebook, scroll to them.
For each of the five, write the finding as a one-line markdown headline using the subject + number + so-what test. Put a sample size on every pay number.
Pick the single best chart for each finding from Sessions 6-7. One chart per point - if two charts say the same thing, drop one.
Add the honest caveat as its own line: pay findings cover only the ~30% of postings that disclose salary.
Build the salary model ★ 9 min · everyone models
Start from your cleaned, salaried frame. Keep three feature columns plus the target: experience_level, a top-locations column, a role bucket, and salary_year.
One-hot encode the categoricals with get_dummies, then split into train and test with a pinned seed.
Expect a modest R2 - somewhere around 0.3 to 0.4 - and an MAE in the tens of thousands. That is not failure; that is honest. Three columns can't capture company, negotiation, or timing. The finding is "role, level, and location together explain about a third of salary spread, and beat guessing the mean by $Xk" - a claim a model earned and a caveat a professional states.
Package and ship ★ 5 min · everyone exports
Reproducibility check: Kernel -> Restart & Run All. Watch it run top to bottom with no errors and every chart re-rendering. If a cell breaks, fix the order now - a report that only runs by hand isn't a report.
Export the notebook to a standalone file anyone can open:
Draft the LinkedIn post - hook, three findings, one chart, the caveat, a soft CTA. Restraint over hype.
Finish it, post it, then keep going ◐ ongoing
- Finish YOUR report - your five findings, your five charts, your model - and actually post it to LinkedIn with your name on it. This is the capstone; the deliverable is a thing that exists in public.
- Try a
RandomForestRegressorin place of LinearRegression (same X, y, split) and compare MAE and R2. It often beats linear on messy data - a first taste of the next level. - Take Kaggle's free Intro to Machine Learning and Intermediate ML micro-courses for the certificate and graded reps - they go deeper on validation, random forests, and cross-validation.
- Read McKinney ch13 - Data Analysis Examples to see full analyses worked end to end.
- polars - when pandas gets slow, polars handles bigger data far faster with a similar API.
- Statistical testing - move from "looks different" to "significantly different" (statsmodels, scipy.stats).
- Dashboards - turn a report into an interactive app with Streamlit so people can explore, not just read.
- The sibling course - learn-sql-with-phoebe for pulling data straight from the warehouse before it ever hits pandas.
Three questions before you graduate 🎯 ◐ 90 seconds
1 · Why split the data into separate train and test sets?
A model can memorize its training rows and look brilliant, then fall apart on anything new. The held-out test set is the only honest scoreboard.
2 · Your model reports an MAE of $18k. What does that mean?
MAE is the mean absolute error in the target's own units. Dollars in, dollars out - no interpretation needed, which is why it's the friendliest metric to report.
3 · What makes a finding shareable, versus just output?
Output is what the computer prints; a finding is what a human should do about it. Subject, number, so-what, caveat - led by the headline. That's the whole last mile.
Official sources covered
This finale teaches the working content of Kaggle's free Intro to Machine Learning micro-course, the scikit-learn basics, and Wes McKinney's open-access Python for Data Analysis (3e). Certificates and graded exercises stay on Kaggle - links provided.