Real datasets have named columns, missing rows, and groups — that is pandas territory. This lesson loads a CSV, cleans it, aggregates by group, and plots the result.
Step 1 — Load and look
import pandas as pd
df = pd.read_csv("results.csv")
print(df.head())
print(df.dtypes)
head() shows the first rows, dtypes the column types. A numeric column that loaded as text (usually a stray unit or comma) is caught right here, before it poisons a plot.
Step 2 — Select and filter
Columns come out by name; rows by a boolean condition:
ok = df[df["quality"] == "good"] # keep the good runs
signal = ok[["time", "signal", "sample"]] # just the columns you need
print(f"{len(ok)} of {len(df)} rows kept")
Step 3 — Aggregate by group
groupby is the workhorse — mean and spread per sample in one line each:
stats = ok.groupby("sample")["signal"].agg(["mean", "std", "count"])
print(stats)
The printed table is worth keeping in the notebook output: it is the numbers your figure claims, stated exactly.
Step 4 — Plot from the frame
DataFrames plot directly through matplotlib:
import matplotlib.pyplot as plt
stats["mean"].plot(kind="bar", yerr=stats["std"], capsize=4)
plt.ylabel("Mean signal")
plt.xlabel("Sample")
plt.xticks(rotation=0)
yerr=stats["std"] puts the error bars on for free, straight from the aggregation you just printed.
Step 5 — Export or insert
Export / Insert as always — vector PDF or straight into the paper with a \label. The script (or notebook) is saved as the figure’s source, so the whole load → filter → aggregate → plot chain reruns next time the data changes.
Tip: Filter early and loudly. Printing “kept N of M rows” beside every filter is one line of code and settles every “wait, why is that point missing?” question a co-author will ever ask.