Statistics for machine learning
Mean, median, mode, variance, standard deviation, and percentiles — computed with the standard library and NumPy.
Why this matters in AI / ML / GenAI
Before you train anything you describe the data. Median versus mean tells you about skew, standard deviation tells you the spread, and p95 latency is the number your SLA is written against. Averages alone hide the problems that matter.
1. Read
Understand the idea in plain English first.
2. Run
Load any example into the compiler and press Run.
3. Change
Edit one value, rerun, and learn from the output.
Centre: mean, median, mode
Mean is the arithmetic average. It is pulled hard by outliers — one 30-second timeout wrecks the mean latency of a hundred fast calls.
Median is the middle value when sorted. It ignores outliers, which is why it is the honest summary for latency, salary, and file sizes.
Mode is the most frequent value, the only one of the three that works on categories like labels.
Rule of thumb: if mean and median differ noticeably, the data is skewed and you should quote the median. Report both when you are describing a dataset to someone else.
Spread: variance and standard deviation
Variance is the mean squared distance from the mean; standard deviation is its square root, back in the original units, which is why it is the one people quote.
Small deviation means values cluster tightly; large means they scatter.
One subtlety that catches people: the divisor. Use n for a population (you have every value) and n - 1 for a sample (you have a subset and are estimating). NumPy's np.std defaults to population (ddof=0), while statistics.stdev uses the sample formula. Pandas' .std() also defaults to sample. Mixing them silently changes your numbers.
In ML, standard deviation drives feature scaling: standardisation is (x - mean) / std, which is what StandardScaler does.
Percentiles and outliers
A percentile is the value below which that share of the data falls. The median is p50.
Latency is always reported as p50, p95, and p99 because the tail is what users feel. A p50 of 200 ms with a p99 of 8 seconds means one request in a hundred is unacceptable, and the mean will never show it.
IQR (interquartile range) is p75 − p25. The standard outlier rule flags anything below p25 - 1.5 * IQR or above p75 + 1.5 * IQR.
Decide deliberately what to do with outliers: they are sometimes corrupt data to drop, and sometimes the fraud cases or the incidents you are actually trying to detect.
Hands-on practice
Compiler on the left. Examples on the right.
On desktop, keep the compiler beside the examples. On mobile, the same blocks stack cleanly. Pick an example, try it in the compiler, then change one small thing.
Summarise a metrics dataset
Live Python compiler
Try it — in-browser Python
Loads when needed: numpy
Add an extreme value to the list and watch mean and p99 move while the median holds.
Code editor
Output
Python runs in your browser. First run downloads the runtime.
Press Run (or Ctrl+Enter) to execute.
Runs CPython in your browser. NumPy, pandas, scikit-learn and Matplotlib load on demand. Charts appear below the output. No input(), no GPU, no network installs.
Clear code examples
Every example is copy-ready. Use Try in compiler when you want to experiment without scrolling around.
Example
Mean vs median with an outlier
One timeout moves the mean by hundreds of milliseconds; the median barely notices.
import statistics
latencies = [180, 210, 195, 205, 190, 30000]
print("mean :", round(statistics.mean(latencies), 1))
print("median:", statistics.median(latencies))
print("mode of labels:", statistics.mode(["pos", "neg", "pos", "pos"]))
without_outlier = latencies[:-1]
print("\nmean without the timeout:", round(statistics.mean(without_outlier), 1))
print("median without it :", statistics.median(without_outlier))
print("\nreport the median when mean and median disagree this much.")Example
Variance, standard deviation, and ddof
Population vs sample divisor — a real source of mismatched numbers.
import statistics
import numpy as np
scores = [0.91, 0.72, 0.85, 0.66, 0.94]
print("population std (numpy default, ddof=0):", round(float(np.std(scores)), 5))
print("sample std (statistics.stdev) :", round(statistics.stdev(scores), 5))
print("numpy with ddof=1 :", round(float(np.std(scores, ddof=1)), 5))
print("variance (sample) :", round(statistics.variance(scores), 5))
mean = float(np.mean(scores))
std = float(np.std(scores))
standardised = [(s - mean) / std for s in scores]
print("\nstandardised:", [round(v, 3) for v in standardised])
print("new mean ~0:", round(float(np.mean(standardised)), 10), "| new std ~1:", round(float(np.std(standardised)), 6))Example
Percentiles for a latency report
p95 and p99 are what your SLA is written against.
import numpy as np
rng = np.random.default_rng(42)
fast = rng.normal(200, 30, 950)
slow = rng.normal(3000, 500, 50)
latencies = np.concatenate([fast, slow])
for label, value in [
("count", len(latencies)),
("mean", np.mean(latencies)),
("p50", np.percentile(latencies, 50)),
("p90", np.percentile(latencies, 90)),
("p95", np.percentile(latencies, 95)),
("p99", np.percentile(latencies, 99)),
("max", np.max(latencies)),
]:
print(f"{label:>6}: {float(value):9.1f}")
print("\nSLA at 1000ms breached by:",
f"{float((latencies > 1000).mean()):.1%} of requests")Example
Outlier detection with the IQR rule
The standard 1.5 x IQR fence, computed in four lines.
import numpy as np
values = np.array([12, 14, 13, 15, 12, 14, 13, 99, 14, 12, -40, 13], dtype=float)
q1, q3 = np.percentile(values, [25, 75])
iqr = q3 - q1
low, high = q1 - 1.5 * iqr, q3 + 1.5 * iqr
mask = (values < low) | (values > high)
print(f"q1={q1} q3={q3} iqr={iqr}")
print(f"acceptable range: {low} to {high}")
print("outliers :", values[mask].tolist())
print("clean :", values[~mask].tolist())
print("mean before:", round(float(values.mean()), 2), "| after:", round(float(values[~mask].mean()), 2))Takeaways
- Median beats mean whenever outliers exist — quote both when describing data.
- NumPy std defaults to population (ddof=0); statistics.stdev and pandas use the sample formula.
- Report p95/p99 for latency; use the 1.5 x IQR fence to find outliers.