orders[orders["zone"] == "Nord"]| order_id | zone | items | total_eur | |
|---|---|---|---|---|
| 0 | 101 | Nord | 2 | 18.5 |
| 2 | 103 | Nord | 3 | 24.0 |
Programming with Python
Friday, the boardroom. Picture it: the investor slides a pencil out of her jacket and sharpens it while you set up, the sound louder than it has any right to be.
Last week you handed her a table she could trust. She read exactly one row, looked up, and said the thing this whole session is about:
“Tables are homework. Charts are arguments.”
Your agent draws any chart in five seconds, so drawing is no longer the hard part. Today: which chart to draw, what it should say, and in which order you show the charts.
On a Mac? Open a terminal now and type:
git --versionA window offers to install the command line developer tools? Click Install and let it run while we talk. It’s a big download, and you need git before today’s lab.
On Windows, or a version number came back? Nothing to do yet.
Ask your agent for a bar chart of revenue per dish:
import matplotlib.pyplot as plt
dishes = ["Pad Thai", "Miso Ramen",
"Bao Box", "Falafel Wrap"]
revenue = [420, 450, 330, 300]
plt.figure() # a fresh canvas
plt.bar(dishes, revenue)
plt.ylabel("Revenue (EUR)")
plt.title("Revenue per dish")
plt.gca() # show it
Read the code, don’t memorize it. Your job: judge the picture. Here, the names already collide.
Problem charts get one of three verdicts (Wilke 2019, ch. 1):
Fix wrong first, then bad. Ugly comes last: a correct, clear chart in default colors already does its job.
Three drafts from Tobi’s agent. Exactly one is wrong:

a) chart 1
b) chart 2
c) chart 3

a) chart 1: shares of one whole add up to 100 %, and these add up to 115 %. That’s wrong. Chart 2 is ugly: loud, but you can read every number. Chart 3 is bad for this question: correct, but nobody can tell which slice is biggest.
A title states the point (Wilke 2019, ch. 22):
| Describes the chart | States the point |
|---|---|
| Revenue per dish | Miso Ramen sells best: 450 euros |
| Delivery times | Most deliveries take 20-30 minutes |
| Order value vs. delivery time | Big orders don’t take longer |
The one chart in MunchCorp’s press release from Episode 8:

Which zone brings MunchCorp the most?
a) Nord, top left
b) Hafen, bottom right
c) Altstadt, bottom left

c) Altstadt: 26.4 % against Nord’s 25.0 %. The eye compares lengths well and areas badly. The sorted bars answer in a second; the pie starts an argument.
| The question | The basic chart |
|---|---|
| How much per category? | bar, sorted |
| How are values spread? | histogram |
| What share of the whole? | pie, for halves and quarters |
| Do two numbers move together? | scatter |
| How does it change over time? | line |
| Where, and how sure? | map, error bars |
A quick tour of each family (Wilke 2019, ch. 5):

Bars start at 0, a dot may zoom: the next block says why.

Also in the directory: cumulative densities, q-q and sina plots.

A shapeless cloud is a finding too: it goes in the title like any claim.

Many lines? One accent, the rest grey. More after the break.

Also in the directory: cartograms, eyes plots, quantile dots.
The same four numbers, two ways. On the left, a bar per number; on the right, a histogram:
values = [8, 12, 9, 15]
plt.bar(range(4), values) # left
plt.hist(values, bins=3) # rightThe bar chart shows 4 bars. How many bars does the histogram show?
a) 4: one bar per number
b) 15: one bar per unit up to the max
c) 3: one bar per range

c) 3: 8 and 9 share the first range, so that bar is 2 tall. A bar chart shows one bar per category, as tall as its value. A histogram shows one bar per range, as tall as the number of values in it.

Miso Ramen vs. Falafel Wrap: how many times as much?
a) about 1.5 times
b) about 3 times
c) about 6 times

a) about 1.5 times: 450 against 300. Cut at 270, the bars keep 180 and 30 of their length, so the picture says 6 times. That’s bad: the numbers are right, the picture misleads. The rule behind it is proportional ink: the ink for a value has to grow with the value (Wilke 2019, ch. 17).
Revenue [710, 718, 724, 731], up 3 % over four weeks. Tobi’s AI drafts the chart and adds one line:
plt.plot([1, 2, 3, 4], [710, 718, 724, 731])
plt.ylim(700, 735)What does the room see?
a) a flat line: 3 % stays 3 % on any axis
b) an error: ylim has to include zero on a line chart
c) a line rising across most of the chart

c) a line rising across most of the chart: the axis spans 35 units and the line rises 21 of them, 60 % of the picture for 3 % of growth. Python raises no error.
The investor reads the axis before the line, and then the title, to see if the two agree.
“Are you faster than the rivals?” First: who was fastest in week 1?

a) MunchCorp
b) Kiezküche
c) Pronto
b) Kiezküche: 30 minutes in week 1. An easy number, found the slow way: you matched four colors to a legend sorted from A to Z. And her real question, whether we got faster, is buried even deeper.
The data is fine. The chart is bad: the point is in there, buried. Four changes dig it out, one at a time, and none of them touches the numbers.

Take the color out of everything except the point (Wilke 2019, ch. 4). One color= per line, and the eye knows where to go.

Names at the lines replace the legend, and they work for the 8 % of men who can’t tell red from green (Wilke 2019, ch. 19).

The title states the claim; frame and tick marks are gone. Same numbers, and every change was a decision.

The rivals only matter as a yardstick: one dashed line for the fastest rival replaces three, and one note marks week 3. Now the chart tells a story.

Every number, line and box here is true, and none of them helps: the point drowns. Balance data and context (Wilke 2019, ch. 23): stop at step 4.

Which problem makes the investor believe something false?
a) the loud colors
b) the axis from 5 %
c) the tilted names

b) the axis. Cut at 5 %, Harburg looked five times as late as Eppendorf; from 0 it is three times. The colors and the tilt only make the chart harder to read. Fix what misleads first.

Sorted, the ranking reads from top to bottom. Lying down, the names need no tilt. The colors meant nothing, so one color is enough, and the legend goes with them.

One accent for the point, the numbers on the bars instead of an axis, the claim on top. Step 1 fixed what misleads, step 2 what confuses, step 3 makes the point.

We promise 35 minutes. Which zone breaks it most often?
a) Altstadt, orange
b) Hafen, green
c) Nord, blue

b) Hafen: three of four deliveries take longer than 35 minutes. One panel per zone shows it, and shows what the overlap hid: Altstadt has two humps, a fast group and a slow one.

One chart instead of four: a box per zone for median and spread, every delivery as a dot beside it, from the most punctual zone to the least.

A reference line turns the chart into a test: every dot above it is a broken promise. The share on top and the title give the number.
A story is facts in a specific order that create a reaction (Wilke 2019, ch. 29). A pitch in four steps:
Each step is one slide with one point, a figure for the generals: the room gets it in seconds.

Opening, challenge, action, resolution: in which order?
a) B, D, C, A
b) A, B, D, C
c) B, C, D, A

a) B, D, C, A: a growing business (B), the slowest in town (D), new routes and a line that falls below every rival (C), customers who come back (A). Then the ask: one more rider keeps it that way.
Option b) leads with the result, an arc that works too, in a newspaper. In a pitch, the problem first makes the result count.
Consistent, not repetitive: same colors and same fonts across the deck, but each slide a different kind of chart, so the room notices that something new is coming.
orders (zone, total_eur), sorted bars, top zone dark red, rest grey, a title that names the leader.”groupbyAI drafts. You decide what goes on the slide.
orders["revenue"], but your column is total_eur: the same confident nonsense you caught in Episode 8.For all three: check the numbers, the axis and the title before a chart goes on a slide.
| Library | What it adds |
|---|---|
| seaborn | box, violin and histogram per group, in one line |
| plotly | charts you can hover over and zoom into |
| folium | street maps in the browser |
| streamlit | a script becomes a web page with sliders |
| networkx | networks of nodes and links |
Next week’s project ideas use all five. Your agent writes the code; the checks from today stay the same.
Season finale next: git and the project kickoff. Your laptop gets git now, before the lab.
Next session opens with Checkpoint 5, and then your project moves to GitHub. An installer that fights you then eats the project kickoff.
So we set up git now, together. Once your setup passes the check, open the lab; I come round and look at every laptop before you leave.
In Zed’s terminal (Ctrl+`), steps 1 and 2 of the one-time setup on the Git Basics page:
git --version, on Windows winget install Git.Gitwinget install GitHub.cli, on a Mac the installer linked on the pagegit --version and gh --version both print a version number? Step 1 is done.
gh auth login
gh auth setup-git
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global pull.rebase falsesetup-git lets git use that login; it prints nothingpull.rebase false: a pull combines your partners’ work with yoursgit --version
gh auth status
git config --global user.emailOpen the lab as soon as all three print. I come round and check.
Stuck? Raise your hand early. Keep your phone at hand: GitHub may ask for a two-factor code, and a password you forgot since Session I is reset in two minutes. Only an iPad? Your GitHub codespace has git and gh built in: run the check in its terminal.
The lab is a script, and a script has no cell that could show a chart. So every chart goes into a picture file:
plt.figure() # OPEN: a fresh canvas, as before
plt.plot(days, revenue)
plt.xlabel("Day")
plt.ylabel("Revenue (EUR)")
plt.title("Day 4 brings the most revenue")
daily_chart = plt.gca() # KEEP: the chart, for the lab's checks
plt.savefig("daily.png") # SAVE: the picture, into python-labs
plt.close() # CLOSE: the canvasplt.close() puts the finished canvas away; the lab checks that none is left open. To look at the picture, click daily.png in Zed’s project panel, the file list on the left.
lab_09_pitch.py from Episode 9: The Pitch Deck into python-labsShow me pitch_deck.png and the last block for feedback. Then practise the Checkpoint 5 hand-in once: open ex_09_d, Cmd/Ctrl+S, menu → Download → Download Python code.
Both are free online. For this session, start with chapters 1, 17 and 29 of Wilke (2019).
Building charts with AI? Revisit the AI Tools page: describe-then-verify is the whole workflow. Matplotlib’s own pyplot tutorial is a friendly next step.
For more, see the literature list of this course.