Plate 29
statistics vs Manual mean: Stdlib Lab
Hands-on statistics vs manual mean/median/stdev lab: real ops/s for fmean, sum/len, and sorted mid versus the stdlib, measured on Linux localhost (lab).
Aditya Challa4 min read
Intro — what this post promises
How expensive is statistics.mean / median / stdev versus a hand sum/len, statistics.fmean, or a sorted mid? This lab times numpy-free reductions on float lists on Linux localhost.
Related links:
- nlargest vs sorted slice localhost lab
- bisect vs linear lookup localhost lab
- perf_counter vs time localhost lab
- array vs list ints localhost lab
- Counter vs dict tally localhost lab
- itertools chain vs flatten localhost lab
- attrgetter vs getattr localhost lab
- frozenset vs set membership localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. No NumPy. Correctness checks vs manual helpers: {'mean_close': True, 'fmean_close': True, 'median_close': True, 'pstdev_close': True}. Affiliates: 0. statistics.mean favors numeric care over micro-speed; fmean is the float fast path.
Verdict up front (N=10 000): sum/len ~209M elem/s vs statistics.mean ~3.4M (~62×); fmean ~24× statistics.mean. Median: manual ≈ statistics (~1.00×). Manual pstdev ~6.3× statistics.pstdev.
Arms
| Arm | Pattern |
|---|---|
statistics.mean | stdlib exact-ish mean |
statistics.fmean | float-optimized mean |
sum(xs)/len(xs) | manual |
| pure loop sum | interpreter loop |
statistics.median / manual sorted mid | both sort |
pstdev / stdev | stdlib vs one-pass-after-mean |
| Welford | online mean+sample stdev |
Lab topology
Script: lab-evidence/69-statistics-vs-manual-mean/results/run_lab.py.
Lead table — mean N=10 000 (p50)
| Arm | red/s | elem/s | ns/red |
|---|---|---|---|
| sum/len | 20,857 | 208.6M | 47945 |
| fmean | 8,027 | 80.3M | 124582 |
| loop sum | 4,178 | 41.8M | 239370 |
| statistics.mean | 338 | 3.4M | 2961878 |
Median & spread N=10 000
| Arm | red/s | elem/s |
|---|---|---|
| statistics.median | 920 | 9.2M |
| manual median | 916 | 9.2M |
| manual pstdev | 1,468 | 14.7M |
| statistics.pstdev | 234 | 2.3M |
| Welford mean+stdev | 1,892 | 18.9M |
Scale — sum/len ÷ statistics.mean
| N | speedup |
|---|---|
| 100 | 90× |
| 1 000 | 68× |
| 10 000 | 62× |
| 100 000 | 62× |
fmean closes much of the gap (~24× vs statistics.mean at N=10 k) while staying in the statistics module.
Reading it
- Hot float means — prefer
statistics.fmeanorsum/len; treatstatistics.meanas the careful/general API. - Median — sorting dominates; stdlib ≈ manual here.
- stdev/pstdev — manual two-pass beat stdlib by ~6× at N=10 k; Welford is competitive when you want one pass.
- Correctness first — for mixed int/Fraction inputs,
statistics.mean’s care may matter more than ns.
Why statistics.mean looks “slow”
On floats, statistics.mean takes a more general numeric path than fmean / a bare sum. That shows up as tens of × in a microbench — fine for reports and mixed types, noisy for a per-request float gauge. If the hot path is “average of floats I already trust,” call fmean (or sum/len) and reserve mean for clarity at API boundaries. Median’s cost is the sort either way; do not expect a helper rename to erase O(n log n).
Pitfalls
- Calling
statistics.meanin a tight float telemetry loop — usefmean. - Re-sorting for median every time on a static array — sort once.
- Sample vs population stdev —
stdevvspstdev(n−1 vs n). - Assuming NumPy is required — stdlib covers a lot; NumPy wins on huge arrays/vectorization (out of scope).
When to pick what
| Need | Prefer |
|---|---|
| Fast float mean | fmean or sum/len |
| Mixed numeric types / docs clarity | statistics.mean |
| Median | statistics.median |
| Online mean+variance | Welford |
| Huge numeric arrays | NumPy (elsewhere) |
Reproduce
Evidence: /workspace/lab-evidence/69-statistics-vs-manual-mean/results/.
Closing
Use fmean for float speed; mean for generality. On this box N=10 k sum/len beat statistics.mean by ~62×, fmean by ~24×, while median stayed a wash and manual pstdev led by ~6×. Match the helper to whether you are optimizing a hot float path or writing clear stats code.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5. N=10k: sum/len mean 209M elem/s vs statistics.mean 3.4M (~62x); fmean ~24x statistics.mean; median ≈ parity; manual pstdev ~6.3x. Affiliates: 0. Evidence: lab-evidence/69-statistics-vs-manual-mean/.
Related links
Plate 17
platform vs os.uname Inventory: Localhost Lab
Hands-on platform.platform vs os.uname host inventory lab: real ops/s plus cache notes, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026
Plate 50
signal vs threading.Event Wakeup: Localhost Lab
Hands-on signal SIGUSR1 vs threading.Event wakeup lab: real p50 latency in microseconds, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026
Plate 76
cmath vs math.hypot Magnitudes: Localhost Lab
Hands-on cmath vs math.hypot magnitude ops lab: real ops/s for abs, polar, and phase, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026