Build a reproducible analysis and communicate its limits
On this page
An analysis is more than the final notebook screen. Another person needs to know which data you used, how you transformed it, what checks passed, and which conclusions the evidence supports. This lesson connects your acquired, validated and explored tables into a repeatable workflow. It assumes familiarity with functions, file paths, grouping and assertions.
Give each stage an explicit input and output
Keep acquisition, validation, transformation and reporting distinguishable. A pure transformation function is easier to examine when it receives a table and returns a new one rather than silently reading global variables or overwriting the raw input. Names should describe the observation and measure, not an unexplained sequence of temporary experiments.
import pandas as pd
def summarize_population(frame):
required = {"country_code", "year", "population"}
if not required.issubset(frame.columns):
raise ValueError("Missing required population columns")
if frame.duplicated(["country_code", "year"]).any():
raise ValueError("Repeated country-year key")
values = pd.to_numeric(frame["population"], errors="raise")
if values.isna().any() or values.lt(0).any():
raise ValueError("Unresolved or negative population")
work = frame.assign(population=values).sort_values(["country_code", "year"])
return work.groupby("country_code").agg(first_year=("year", "min"), last_year=("year", "max"), observations=("year", "size"))
from pathlib import Path
population = pd.read_csv(Path("data/completion/worldbank/population.csv"))
coverage = summarize_population(population)
print(coverage)
assert coverage["observations"].eq(6).all() first_year last_year observations
country_code
DEU 2018 2023 6
FRA 2018 2023 6
GBR 2018 2023 6
The function checks a small contract before reporting coverage. It does not validate every possible issue; for example, you still need the producer’s definition and the units. Make the scope of each check explicit. A passed assertion is evidence about a specified property, not a certificate that an entire investigation is correct.
Check failure as well as success
A useful pipeline fails clearly when its assumptions are violated. Try a duplicate key, missing required column and unresolved value in a small disposable copy. Verify that failure occurs before a report is exported. Never make the source data “pass” by deleting inconvenient records without a documented reason.
broken = pd.concat([population, population.iloc[[0]]], ignore_index=True)
try:
summarize_population(broken)
except ValueError as error:
print(str(error))
else:
raise AssertionError("Duplicate key was not rejected")Repeated country-year key
The same discipline applies to a notebook: restart, run all cells in order, inspect warnings and compare a few results with independent arithmetic. Record Python and package versions and any randomness. If a live acquisition fails, identify that stage; do not quietly substitute an old table without labelling the substitution.
Scale the question before scaling infrastructure
Read only the needed columns, select appropriate types and filter early when the format supports it. Chunking can help when an operation can be combined from partial results. A global median, a join across partitions and a time window require more care than adding independent counts. The Pandas scaling guide discusses memory-aware choices and their limits.
path = Path("data/completion/worldbank/population.csv")
rows = 0
for chunk in pd.read_csv(path, usecols=["country_code", "year", "population"], chunksize=5):
rows += len(chunk)
print(rows)
assert rows == 1818
This demonstration uses a tiny file to make the process visible; it is not a performance benchmark. To combine means from chunks, retain sums and counts rather than averaging chunk means equally. Check whether chunk boundaries split an entity or time sequence before applying a grouped or rolling operation.
Write a report that separates findings from interpretation
A useful report states the question, data producer and snapshot, inclusion rules, observation unit, method, results and limitations. Report denominators and missingness alongside comparisons. Distinguish what the table shows from what you infer might explain it. A result can be reproducible and still be limited by selection, measurement or an unsuitable question.
Use a short narrative: one supported claim, the evidence for it, the most important alternative interpretation, and the next information needed. Preserve exact values in an accompanying table, while rounding the narrative to a precision meaningful for the question. Do not add decimal places merely because software produced them.
Prepare outputs for another reader
Export a tidy table, data dictionary, provenance record, cleaning log and run instructions. Include the checks and the unresolved cases. Keep instructor answers and exploratory private notes separate from the learner or stakeholder package. A chart-ready table should identify its observation key, category labels, time coverage, units and missingness; Book 3 will develop how to turn that evidence into clear figures.
Practice and independent check
Write a function that produces a latest-year comparison only after verifying that every selected country has that year. Demonstrate one failing fixture and one successful fixture. Then draft a 250-word analysis memo supported by the saved World Bank snapshot. Your memo should not infer causes of population change from six annual totals. Acknowledge that estimates can be revised and that the three-country selection cannot represent the world.
Transition
A reproducible report completes an analysis for its current question. Preparing data for prediction adds a new constraint: what information will exist at prediction time, and how will evaluation remain independent? The next lesson introduces that handoff without moving model training into this book.