Квартили, межквартильный размах и перцентили
Если разбить упорядоченные данные на равные доли, мы получим устойчивую к выбросам карту распределения.
Перцентиль p — значение, ниже которого лежит p% данных. 90-й перцентиль зарплат — порог, который не превышают 90% сотрудников.
Квартили: три точки, четыре части
Квартили делят упорядоченные данные на четыре равные части:
- Q1 (первый квартиль, 25-й перцентиль) — ниже него 25% данных;
- Q2 — это медиана, 50-й перцентиль;
- Q3 (третий квартиль, 75-й перцентиль) — ниже него 75% данных.
В модуле statistics есть функция quantiles: по умолчанию она режет данные на 4 части и возвращает три точки разреза — это и есть Q1, Q2, Q3.
from statistics import quantiles, median
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
q1, q2, q3 = quantiles(data, n=4)
print("Q1:", q1)
print("Q2 (медиана):", q2, "/ median:", median(data))
print("Q3:", q3)
Вывод:
Q1: 3.25 Q2 (медиана): 6.5 / median: 6.5 Q3: 9.75
Примечание: способов считать квартили несколько, и разные инструменты могут давать чуть разные числа на концах. Модуль statistics по умолчанию использует метод «exclusive»; для аналитики это вполне годится, главное — пользоваться одним методом последовательно.
Межквартильный размах (IQR)
IQR (Interquartile Range) — это разница Q3 − Q1, то есть размах «средних 50%» данных. Он показывает, насколько разбросана центральная масса, и при этом игнорирует верхние и нижние 25% — а значит, устойчив к выбросам, в отличие от обычного размаха.
from statistics import quantiles
# Те же данные, но добавили дикий выброс
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1000]
q1, q2, q3 = quantiles(data, n=4)
iqr = q3 - q1
print("Q1:", q1, "Q3:", q3)
print("IQR:", iqr)
print("Обычный размах:", max(data) - min(data))
Вывод:
Q1: 3.25 Q3: 9.75 IQR: 6.5 Обычный размах: 999
Видите разницу? Выброс 1000 раздул обычный размах до 999, а IQR остался 6.5 — он почти не заметил аномалию. Поэтому IQR — надёжная мера разброса для «грязных» данных.
Произвольные перцентили
Перцентили нужны не только на четвертях. В вебе и нагрузочном тестировании постоянно смотрят на 90-й, 95-й и 99-й перцентили времени ответа: «95% запросов обрабатываются быстрее, чем за X». Чтобы получить, например, 90-й перцентиль, режем данные на 100 частей и берём 90-ю границу.
from statistics import quantiles
# Время ответа сервиса в миллисекундах
times = [50, 52, 55, 60, 61, 63, 70, 75, 80, 500]
cuts = quantiles(times, n=100) # 99 точек: 1-й..99-й перцентили
p90 = cuts[89] # индекс 89 -> 90-й перцентиль
print("90-й перцентиль времени ответа:", p90, "мс")
Вывод:
90-й перцентиль времени ответа: 458.0 мс
Здесь 90-й перцентиль высокий из-за одного медленного запроса в 500 мс — это сигнал, что у части пользователей всё плохо, даже если медиана выглядит хорошо. Среднее и медиана такой «хвост» легко прячут, а перцентили его обнажают.
Пятичисловая сводка
Минимум, Q1, медиана, Q3, максимум вместе называют пятичисловой сводкой (five-number summary) — компактный портрет распределения, на котором строится «ящик с усами» (boxplot).
| Число | Смысл |
| минимум | наименьшее значение |
| Q1 | граница нижних 25% |
| медиана | середина |
| Q3 | граница верхних 25% |
| максимум | наибольшее значение |
Итог
- Квартили Q1, Q2, Q3 делят данные на четыре равные части; Q2 — это медиана.
- IQR = Q3 − Q1 — разброс центральных 50%, устойчивый к выбросам.
- Перцентили (90-й, 95-й, 99-й) показывают «хвосты», которые прячут среднее и медиана.
statistics.quantilesрежет данные на нужное число частей.