Exercises — Jupyter notebooks workflow
Before we start, one picture ties the whole page together: what "the kernel remembers" actually means.

Level 1 — Recognition
Goal: name the parts and read the signals Jupyter shows you.
Exercise 1.1
A cell shows In [ ] (empty brackets, no number). What does the empty bracket tell you, and what are the two distinct situations that both produce it?
Recall Solution
Empty brackets mean the cell has never been run in the current kernel session. The two situations:
- Fresh notebook / just-restarted kernel — no cell has run yet, so nothing has a number.
- A newly created cell you added but have not executed, even though other cells around it have numbers.
Contrast: In [*] (an asterisk) means the cell is currently running — the kernel is busy. In [7] means it finished as the 7th execution.
Exercise 1.2
You see this on screen, top to bottom:
In [5] … In [2] … In [9].
Put these three cells in the order they were actually executed, and say what this pattern warns you about.
Recall Solution
The counter records execution order, so sort by the number: 2 → 5 → 9. On the page they sit in order 5, 2, 9 (top to bottom). Because the page order (5,2,9) is not the run order (2,5,9), the notebook was run out of order. Warning: a later cell may depend on a variable that only exists because you ran cells in this scrambled sequence. A fresh top-to-bottom run might fail.
Level 2 — Application
Goal: predict the exact printed output of a cell.
Exercise 2.1
A cell contains:
a = 3
b = 4
a + b
a * bWhat renders below the cell, and why only that?
Recall Solution
Jupyter auto-displays only the value of the last expression in a cell. The last line is a * b, so it shows:
The line a + b (value ) is computed and immediately thrown away — nothing else prints because there is no print(...) on it. If you wanted both, you would write print(a + b) then leave a * b as the final line.
Exercise 2.2
Two cells, run in this order:
Cell A
x = 5Cell B
y = x * 2
yWhat does Cell B display? Now the kernel is restarted and you run only Cell B. What happens?
Recall Solution
First run: after Cell A, the namespace is . Cell B computes so it displays .
After restart, the namespace is a fresh empty dictionary: . Running Cell B alone asks for x, which is not a key. Result: a NameError: name 'x' is not defined. The value lived only in memory, and restart wiped it.
Level 3 — Analysis
Goal: trace state through a messy execution order and find the bug.
Exercise 3.1
A student ran cells in this exact sequence (the number is the execution counter shown):
| Ran | Cell contents |
|---|---|
In [1] |
data = load() |
In [2] |
total = data.sum() |
In [3] |
data = clean(data) |
In [4] |
(re-runs Cell 2) total = data.sum() |
Is the value of total after In [4] computed from the raw data or the cleaned data? Explain using the namespace.
Recall Solution
Track the key data in the namespace over time:
- After
In [1]:data→ raw. In [2]:totalcomputed from raw.In [3]:datais reassigned → cleaned. The raw version is gone; nothing still points to it.In [4]re-runs the sum: it reads the currentdata, which is now the cleaned version.
So after In [4], total is from the cleaned data. Key idea: a cell always reads the latest value in the namespace, not the value that existed when you first wrote the cell.
Exercise 3.2
A notebook, read top to bottom on the page, looks like this:
# Cell 1
print(result) # In [4]
# Cell 2
result = 42 # In [3]It runs without error on the author's screen. A collaborator hits Restart & Run All and gets an error. Name the error and the exact fix, and explain why the author never saw it.
Recall Solution
The counters (In [4] above In [3]) reveal Cell 2 was run before Cell 1. So when Cell 1 printed result, the value already sat in the namespace — no error for the author.
"Restart & Run All" empties the namespace, then runs top to bottom: Cell 1 first. At that moment result does not exist yet → NameError: name 'result' is not defined.
Fix: reorder so the definition comes above the use (Cell 2's code before Cell 1's), then Restart & Run All to confirm a clean top-to-bottom pass.
Level 4 — Synthesis
Goal: design a clean, fast, reproducible notebook layout.
Exercise 4.1
You have one blob cell that takes 15 minutes total: load data (2 min), clean (3 min), train model (10 min). The model step keeps failing and you keep re-running the blob. Split it into cells and compute the time cost of one more debug cycle before and after the split.
Recall Solution
Before (one blob): every failed attempt re-runs everything.
After (three cells): load and clean already succeeded and their results (data, data_clean) sit in the namespace. To retry the model you re-run only the model cell:
Saving per cycle: Over, say, 6 debug attempts you save minutes. The dependencies live in memory, so you never pay for them twice.
Exercise 4.2
Using the EDA flow from the parent, arrange these five actions into five separate cells in the correct order, and for each cell state the one conceptual job it does:
imports · fit a LinearRegression on ['ad_spend', 'season'] · read_csv · data.describe() · histogram of revenue.
Recall Solution
Order follows the data's life cycle — you cannot explore data you haven't loaded, nor model features you haven't seen.
| Cell | Code job | One conceptual thing |
|---|---|---|
| 1 | import pandas as pd, import numpy as np, matplotlib |
bring in tools |
| 2 | data = pd.read_csv('sales.csv') |
load data |
| 3 | data.describe() |
numeric summary (last expr auto-shows) |
| 4 | data['revenue'].plot(kind='hist') |
visual summary |
| 5 | LinearRegression().fit(data[['ad_spend','season']], y) |
model |
Each cell does exactly one thing, so if cell 5 fails you re-run only cell 5 — cells 1–4 are still cached in the namespace. See 1.5.01-Pandas-DataFrames and 1.6.01-Matplotlib-basics for the tools in cells 2–4.
Level 5 — Mastery
Goal: reason about the whole system — memory, reproducibility, and edge cases.
Exercise 5.1
The execution counter is defined as
Start from a fresh kernel. You run: Cell A, Cell B, Cell A again, Cell C. Write the In [n] shown on each cell at the end.
Recall Solution
Apply the rule step by step (fresh kernel → max starts effectively at ):
| Action | new | A | B | C |
|---|---|---|---|---|
| run A | 1 | 1 | – | – |
| run B | 2 | 1 | 2 | – |
| run A | 3 | 3 | 2 | – |
| run C | 4 | 3 | 2 | 4 |
Final labels displayed: A shows In [3], B shows In [2], C shows In [4].
Notice A's first number () was overwritten when it re-ran; a cell only ever shows its most recent execution number.
Exercise 5.2
A cell mutates a list in place and is run twice:
nums = [1, 2, 3]
nums.append(nums[-1] + 1)
numsCell 1 defines nums; Cell 2 is the append line above. You run Cell 1 once, then run Cell 2 three times. What is the final value of nums? Then predict the value if you had instead run "Restart & Run All" (each cell once).
Recall Solution
Repeated Cell 2 (no restart): nums persists in the namespace and each run appends one element built from the current last element.
- start:
- run 1: last append
- run 2: last append
- run 3: last append
Final: — length . Re-running a mutating cell accumulates side effects because the object survives between runs.
Restart & Run All (each once): namespace reset; Cell 1 rebuilds nums = [1,2,3], Cell 2 appends once:
The gap between and is exactly why "works for me" fails for others — hidden repeated mutations. This is a core lesson for debugging notebooks.
Exercise 5.3 (edge case)
An empty kernel: you run a cell that contains only a comment # TODO. What is displayed below it, and what In [n] does it get?
Recall Solution
A comment-only cell has no expression to evaluate, so nothing renders below it — the output area stays blank (not None, not an error).
It still counts as an execution: from an empty kernel it gets In [1]. The counter tracks runs, and running an empty cell is still a run. Edge takeaway: "no output" and "did not run" look different — check the bracket (In [1] vs In [ ]) to tell them apart.
Recall
Quick self-check reveals:
In [*] means ::: the cell is currently running (kernel busy).
A restarted kernel's namespace is ::: a fresh empty dictionary — all variables gone.
Auto-display shows ::: only the value of the last expression in the cell.
"Done" for sharing means ::: passes Restart & Run All top-to-bottom with no error.
Re-running a mutating cell ::: accumulates side effects because the object survives between runs.