Monthly Stats files store the full percentile distribution of each metric across all NDT tests in a given geography and month. Understanding what percentiles mean β and a critical quirk in how latency and loss are stored β is essential for correct analysis.
Try it yourself: Open the country-level explorer on Binder
What a Percentile Means Here
A percentile column metric_pN answers: βWhat was the metric value at or below which N% of tests fell?β
For example, download_p50 is the median download speed β half of all NDT tests in that geography/month were slower, half were faster.
Available percentiles in Monthly Stats: 1, 5, 10, 25, 50, 75, 90, 95, 99.
The distribution tells a richer story than a single number
| Percentile | What it represents |
|---|---|
p50 | Typical experience β the median user |
p25βp75 | The βmiddle 50%β β the interquartile range |
p95 / p99 | Near-best performance β the top 5% or 1% of tests |
p5 / p1 | Near-worst performance β the bottom 5% or 1% |
A wide gap between p25 and p75 signals high variability β some users have very fast connections while others are very slow. A narrow band means more uniform service.
The Polarity Quirk: Latency and Loss Are Inverted
This is the most important thing to understand about Monthly Stats percentiles.
For download and upload speed, higher is better. download_p95 is the 95th percentile of speed β only 5% of tests were faster. It represents near-best performance.
For latency and loss, lower is better. But the percentile numbering still follows the same convention β latency_p95 is the 95th percentile of latency values, meaning 95% of tests had lower latency. It therefore represents near-worst latency (only 5% of tests had higher latency).
In summary:
| Metric | p5 means | p95 means |
|---|---|---|
| Download / Upload | Near-worst speed | Near-best speed |
| Latency | Near-best latency (lowest values) | Near-worst latency (highest values) |
| Loss | Near-best loss (lowest values) | Near-worst loss (highest values) |
Why does this matter?
If you naively plot latency_p95 alongside download_p95 expecting both to represent βgoodβ performance, you will draw the wrong conclusions. Geographies with high latency_p95 have worse latency, not better.
The Internet Quality Barometer (IQB) handles this by applying the polarity correctly when computing scores: it uses download_p95 (high = good) but latency_p5 or latency_p50 (low = good) as inputs.
Code example
# Correct: for latency, LOWER values are better, so nsmallest finds best
best_latency = df.nsmallest(10, "latency_p50")[["country_code", "latency_p50"]]
# Correct: for download, HIGHER values are better
best_download = df.nlargest(10, "download_p50")[["country_code", "download_p50"]]
# Wrong: this finds countries with worst latency, not best
worst_approach = df.nlargest(10, "latency_p50") # β these are the SLOWEST countries
Which Percentile Should I Use?
The right choice depends on your question:
Use p50 (median) when you want the typical user experience. The median is robust to outliers and represents a realistic βwhat does this network feel like for most people?β summary.
Use p95 for download/upload when you want near-peak performance β useful for understanding what fast connections look like in an area. The IQB framework uses p95 download/upload as an optimistic baseline for its quality thresholds.
Use p5 for latency/loss when you want near-best latency β the lowest latency achieved by 95% of users. This is the IQB convention.
Use p25βp75 to understand variability and equity. A location where download_p75 / download_p25 is very large has high inequality between its fast and slow users.
Sample Count and Reliability
The sample_count column records how many NDT tests contributed to each row. Rows with very few tests have noisy percentiles:
# Filter out low-confidence rows before analysis
df_reliable = df[df["sample_count"] >= 100]
As a rough guide:
- < 30 tests: very unreliable β avoid using percentile estimates
- 30β100 tests: usable with caution, especially for p50
- > 100 tests: generally reliable for p25βp75
- > 500 tests: reliable for extreme percentiles (p5, p95)
Finer-grained slices (city, city+ASN) will have far more low-count rows than country-level data, especially for smaller cities or minority ISPs.
Further Reading
- M-Lab Monthly Stats Dataset β dataset overview, access, and structure
- Beyond Speed: Understanding Internet Quality Metrics β why latency and loss matter as much as speed
- NDT (Network Diagnostic Tool) β how the underlying measurements are made