Percentiles and Spread
Interquartile Range
The IQR is Q3 − Q1: the width of the middle 50% of sorted data. It is
robust to outliers because the extreme 25% on each end are excluded. Sort,
compute the two quartile positions (exact integers for n=9), read sv[pos],
subtract. With scipy, scipy.stats.iqr(x) uses linear interpolation
(matching np.percentile) by default.
By hand
With the library
scipy.stats.iqr(x) defaults to rng=(25, 75) and linear interpolation,
matching np.percentile. With exact-integer positions the result is the
same as the naive subtraction. Showing Q1 and Q3 separately makes the
denominator and subtraction visible.
naive.py
values = [4, 7, 2, 9, 1, 8, 5, 6, 3]
sv = sorted(values)
n = len(sv)
pos_q1 = int((n - 1) * 0.25)
pos_q3 = int((n - 1) * 0.75)
q1 = sv[pos_q1]
q3 = sv[pos_q3]
iqr = q3 - q1
print('RESULT:', iqr)
library.py
import numpy as np
from scipy.stats import iqr
from dalib.display import set_display
set_display()
x = [4, 7, 2, 9, 1, 8, 5, 6, 3]
q1, q3 = np.percentile(x, [25, 75])
result = iqr(x)
print('Q1:', int(q1))
print('Q3:', int(q3))
print('RESULT:', int(result))
Q1: 3
Q3: 7
RESULT: 4
Implementation notes
- IQR is robust: adding a large outlier shifts Q1/Q3 only if it pushes into the middle 50%, which is rare. Compare to std, which grows with every outlier.
scipy.stats.iqraccepts arngparameter if you want a different percentile spread (e.g.rng=(10, 90)for the interdecile range).- Cross-reference:
median-and-quartiles(this chapter) for the quartile positions;iqr-outlier-flags(python-data-cleaning ch06) for using IQR to flag outliers via the 1.5×IQR fence rule.