Percentiles and Spread
Five-Number Summary
Describe a dataset's spread with five landmarks: minimum, Q1, median, Q3,
and maximum. With n=9, percentile positions (n−1)×p/100 for p∈{0,25,50,75,
100} land on exact integers (0, 2, 4, 6, 8) — no interpolation. Loop over
the five percents, compute each position, read sv[pos]. With numpy,
np.percentile(x, [0,25,50,75,100]) gives the same five values via linear
interpolation (exact at integer positions).
By hand
n=9 gives positions 0, 2, 4, 6, 8 for p=0,25,50,75,100 — all integers.
Loop over percents=[0,25,50,75,100], compute pos = int((n-1)*p/100),
append sv[pos]. Unpack: min=1, Q1=3, median=5, Q3=7, max=9.
values = [4, 7, 2, 9, 1, 8, 5, 6, 3]
sv = sorted(values)
n = len(sv)
percents = [0, 25, 50, 75, 100]
summary = []
for p in percents:
pos = int((n - 1) * p / 100)
summary.append(sv[pos])
lo, q1, median, q3, hi = summary
print('RESULT:', (lo, q1, median, q3, hi))
values ← [4, 7, 2, 9, 1, 8, 5, 6, 3]
1values = [4, 7, 2, 9, 1, 8, 5, 6, 3]2sv = sorted(values)values this step[4, 7, 2, 9, 1, 8, 5, 6, 3]valuessv ← [1, 2, 3, 4, 5, 6, 7, 8, 9]
1values = [4, 7, 2, 9, 1, 8, 5, 6, 3]2sv = sorted(values)3n = len(sv)values this step[1, 2, 3, 4, 5, 6, 7, 8, 9]svn ← 9
2sv = sorted(values)3n = len(sv)4percents = [0, 25, 50, 75, 100]values this step9npercents ← [0, 25, 50, 75, 100]
3n = len(sv)4percents = [0, 25, 50, 75, 100]5summary = []values this step[0, 25, 50, 75, 100]percentssummary ← []
4percents = [0, 25, 50, 75, 100]5summary = []6for p in percents:values this step[]summaryp ← 0
5summary = []6for p in percents:7 pos = int((n - 1) * p / 100)values this step0ppos ← 0
6for p in percents:7 pos = int((n - 1) * p / 100)8 summary.append(sv[pos])values this step0possummary ← [1]
7 pos = int((n - 1) * p / 100)8 summary.append(sv[pos])9lo, q1, median, q3, hi = summaryvalues this step[] → [1]summaryp ← 25
5summary = []6for p in percents:7 pos = int((n - 1) * p / 100)values this step0 → 25ppos ← 2
6for p in percents:7 pos = int((n - 1) * p / 100)8 summary.append(sv[pos])values this step0 → 2possummary ← [1, 3]
7 pos = int((n - 1) * p / 100)8 summary.append(sv[pos])9lo, q1, median, q3, hi = summaryvalues this step[1] → [1, 3]summaryp ← 50
5summary = []6for p in percents:7 pos = int((n - 1) * p / 100)values this step25 → 50ppos ← 4
6for p in percents:7 pos = int((n - 1) * p / 100)8 summary.append(sv[pos])values this step2 → 4possummary ← [1, 3, 5]
7 pos = int((n - 1) * p / 100)8 summary.append(sv[pos])9lo, q1, median, q3, hi = summaryvalues this step[1, 3] → [1, 3, 5]summaryp ← 75
5summary = []6for p in percents:7 pos = int((n - 1) * p / 100)values this step50 → 75ppos ← 6
6for p in percents:7 pos = int((n - 1) * p / 100)8 summary.append(sv[pos])values this step4 → 6possummary ← [1, 3, 5, 7]
7 pos = int((n - 1) * p / 100)8 summary.append(sv[pos])9lo, q1, median, q3, hi = summaryvalues this step[1, 3, 5] → [1, 3, 5, 7]summaryp ← 100
5summary = []6for p in percents:7 pos = int((n - 1) * p / 100)values this step75 → 100ppos ← 8
6for p in percents:7 pos = int((n - 1) * p / 100)8 summary.append(sv[pos])values this step6 → 8possummary ← [1, 3, 5, 7, 9]
7 pos = int((n - 1) * p / 100)8 summary.append(sv[pos])9lo, q1, median, q3, hi = summaryvalues this step[1, 3, 5, 7] → [1, 3, 5, 7, 9]summaryfor p in percents:
5summary = []6for p in percents:7 pos = int((n - 1) * p / 100)hi ← 9, lo ← 1, median ← 5, q1 ← 3, q3 ← 7
8 summary.append(sv[pos])9lo, q1, median, q3, hi = summary10print('RESULT:', (lo, q1, median, q3, hi))values this step9hi1lo5median3q17q3stdout ← RESULT: (1, 3, 5, 7, 9)
9lo, q1, median, q3, hi = summary10print('RESULT:', (lo, q1, median, q3, hi))values this stepRESULT: (1, 3, 5, 7, 9)stdout
With the library
np.percentile(x, [0,25,50,75,100]) extends the same linear-interpolation
method to all five percentiles. Percentile 0 returns the minimum and 100
returns the maximum — no separate min()/max() calls needed. Values are
float; cast with int() to match.
import numpy as np
from dalib.display import set_display
set_display()
x = [4, 7, 2, 9, 1, 8, 5, 6, 3]
lo, q1, median, q3, hi = np.percentile(x, [0, 25, 50, 75, 100])
print('min:', int(lo))
print('Q1:', int(q1))
print('median:', int(median))
print('Q3:', int(q3))
print('max:', int(hi))
print('RESULT:', (int(lo), int(q1), int(median), int(q3), int(hi)))
min: 1
Q1: 3
median: 5
Q3: 7
max: 9
RESULT: (1, 3, 5, 7, 9)
Implementation notes
- The five-number summary is the data behind a box-and-whisker plot: the box spans Q1–Q3, the line inside is the median, and the whiskers extend to min and max (before outlier clipping).
np.percentilewith p=0 and p=100 is equivalent tomin()andmax(); calling them through percentile keeps the loop uniform and avoids two extra passes.- Cross-reference:
median-and-quartiles(this chapter) for the quartile position derivation;interquartile-range(this chapter) for the Q3−Q1 spread derived from this summary.