Percentiles and Spread
Median and Quartiles
Split sorted data into four equal parts using Q1, median (Q2), and Q3.
With n=9, the percentile positions (n−1)×p/100 land on exact integers (2,
4, 6), so no interpolation is needed — just index into the sorted list.
Loop over [25, 50, 75], compute each integer position, read sv[pos]. With
numpy, np.percentile(x, [25, 50, 75]) uses linear interpolation by
default; with exact-integer positions the result is identical.
By hand
With the library
np.percentile(x, [25, 50, 75]) uses linear interpolation: position
= (n−1)×p/100; if fractional, it blends adjacent elements. With n=9 the
positions are exact integers so no blending occurs and the result equals the
naive index lookup. Values are float — cast with int() to match.
naive.py
values = [4, 7, 2, 9, 1, 8, 5, 6, 3]
sv = sorted(values)
n = len(sv)
percents = [25, 50, 75]
quartiles = []
for p in percents:
pos = int((n - 1) * p / 100)
quartiles.append(sv[pos])
q1, median, q3 = quartiles
print('RESULT:', (q1, median, q3))
library.py
import numpy as np
from dalib.display import set_display
set_display()
x = [4, 7, 2, 9, 1, 8, 5, 6, 3]
q1, median, q3 = np.percentile(x, [25, 50, 75])
print('Q1:', int(q1))
print('median:', int(median))
print('Q3:', int(q3))
print('RESULT:', (int(q1), int(median), int(q3)))
Q1: 3
median: 5
Q3: 7
RESULT: (3, 5, 7)
Implementation notes
- The integer-position guarantee: choose n such that (n−1) is divisible by
4 (e.g. n=5→4, n=9→8, n=13→12). Then 0.25×(n−1), 0.5×(n−1), and
0.75×(n−1) are all integers and
int(...)is an exact match to numpy's linear interpolation. - When positions are not integers, numpy interpolates:
sv[floor(pos)] + frac × (sv[floor(pos)+1] − sv[floor(pos)]). Replicating this exactly in naive code requires the same formula. - Cross-reference:
median-search(ch01) for finding the median alone;interquartile-range(this chapter) for using Q1 and Q3 to measure spread.