Среднее, медиана и перцентили: что когда брать

«Средняя зарплата в компании 202 тысячи. Хорошая ли это метрика?» — простой на вид вопрос с двойным дном.

Медиана — значение, которое делит упорядоченный ряд пополам: половина наблюдений меньше, половина больше. В отличие от среднего, она не реагирует на величину крайних значений.

Вопрос 1. Когда среднее врёт, а медиана — нет?

Что проверяет интервьюер. Не формулу, а чувство данных. Аналитик, который автоматически ставит AVG в каждый дашборд, однажды покажет бизнесу «средний чек 48 тысяч» при том, что 90% клиентов платят меньше 20.

import statistics

salaries = [70, 75, 80, 85, 90, 95, 100, 110, 120, 1200]

print("Среднее:", statistics.mean(salaries))
print("Медиана:", statistics.median(salaries))
print("Среднее без топ-1:", round(statistics.mean(salaries[:-1]), 1))

Вывод:

Среднее: 202.5
Медиана: 92.5
Среднее без топ-1: 91.7

Девять человек из десяти получают меньше «средней зарплаты». Одна зарплата основателя сдвинула среднее в два с лишним раза, а медиана осталась там, где живёт коллектив. Формально это свойство называется устойчивостью (робастностью): чтобы сломать медиану, нужно испортить половину данных, чтобы сломать среднее — достаточно одного значения.

СитуацияЧто брать
Скошенное распределение: чеки, зарплаты, время на сайте, LTVмедиана (и перцентили)
Симметричные данные без тяжёлых хвостовсреднее — оно точнее и стабильнее
Нужно, чтобы метрика складывалась: выручка = средний чек × число чековсреднее — медиана так не работает
Категории: любимый способ оплаты, топ-браузермода
SLA, скорость ответа, время загрузкиперцентили p95 / p99

Последняя строка важна отдельно. Среднее время ответа сервиса 200 мс звучит отлично, но если p99 равен 8 секундам, то каждый сотый пользователь ждёт восемь секунд — и именно он пишет в поддержку. Поэтому в мониторинге живут перцентили, а не среднее.

Вопрос 2. Что такое перцентили и как их читать?

p-й перцентиль — значение, ниже которого лежит p процентов наблюдений. Медиана — это p50. Квартили режут выборку на четыре части: Q1 = p25, Q2 = p50, Q3 = p75.

import statistics

times = [120, 130, 140, 150, 160, 170, 180, 200, 230, 260,
         280, 300, 340, 400, 460, 520, 700, 1200, 2400, 8000]

q1, med, q3 = statistics.quantiles(times, n=4)
p90, p95 = statistics.quantiles(times, n=20)[17], statistics.quantiles(times, n=20)[18]

print("Среднее:", statistics.mean(times), "мс")
print("Медиана (p50):", med, "мс")
print("Q1 / Q3:", q1, "/", q3, "мс")
print("p90:", p90, "| p95:", p95, "мс")

Вывод:

Среднее: 817 мс
Медиана (p50): 270.0 мс
Q1 / Q3: 162.5 / 505.0 мс
p90: 2280.0 | p95: 7720.0 мс

Половина запросов укладывается в 270 мс, но каждый двадцатый ждёт около восьми секунд. Среднее в 817 мс не описывает ни одну из этих двух реальностей — оно висит в пустоте между ними.

Вопрос 3. Как понять, что распределение скошено, не рисуя график?

Простой практический признак: сравнить среднее и медиану.

  • Среднее заметно больше медианы — правый хвост, «длинный хвост» больших значений. Так устроены почти все денежные метрики.
  • Среднее меньше медианы — левый хвост. Встречается реже: например, оценки, упирающиеся в потолок в 5 баллов.
  • Среднее ≈ медиана — распределение примерно симметрично.

И отдельно — предупреждение о бимодальности. Если у распределения два пика (например, бесплатные пользователи со средним чеком 0 и платные со средним 5000), то ни среднее, ни медиана не описывают выборку: они показывают точку, в которой не находится почти никто. Правильный ход — разделить сегменты и считать метрику отдельно в каждом.

Вопрос 4. Можно ли усреднить средние по регионам, чтобы получить среднее по стране?

Нет — если размеры регионов различаются. Это подвох, который любят добавлять в конце.

regions = [
    ("Москва",   1000, 4200),   # (регион, число заказов, средний чек)
    ("Казань",    120, 1800),
    ("Пермь",      80, 1500),
]

naive = sum(avg for _, _, avg in regions) / len(regions)
total_orders = sum(n for _, n, _ in regions)
total_revenue = sum(n * avg for _, n, avg in regions)
weighted = total_revenue / total_orders

print("Среднее по средним:", round(naive, 1))
print("Взвешенное среднее:", round(weighted, 1))

Вывод:

Среднее по средним: 2500.0
Взвешенное среднее: 3780.0

Разница почти в полтора раза: наивный расчёт дал Перми с восемьюдесятью заказами тот же вес, что и Москве с тысячей. Правило простое: среднее считают из суммы и количества, а не из чужих средних. Если исходные данные недоступны, нужно взвешенное среднее с весами-объёмами.

Типичные ошибки кандидатов

  • Усредняют средние по регионам или по дням, не взвешивая их на объём.
  • Отвечают «медиана лучше среднего» без оговорок. Не лучше — устойчивее; у среднего есть свойство аддитивности, которого у медианы нет.
  • Не могут объяснить, почему нельзя посчитать выручку как «медианный чек × число чеков».
  • Забывают про перцентили, хотя именно они нужны для метрик скорости и SLA.
  • Считают среднее по бимодальной выборке и делают вывод о «типичном пользователе».
  • Путают моду с самым большим значением.

Как ответить кратко

«Среднее чувствительно к крайним значениям — одного крупного клиента хватит, чтобы сдвинуть его в разы. Медиана устойчива: чтобы её сломать, нужно испортить половину данных. Поэтому для денежных и временных метрик — чеков, зарплат, LTV, времени ответа — я беру медиану и перцентили, а среднее оставляю там, где распределение симметрично или где метрика должна складываться: выручка = средний чек × число чеков, с медианой так не получится. Быстрый признак скошенности — среднее заметно больше медианы. И если у распределения два пика, я не усредняю вообще, а разделяю сегменты».

Проверьте себя
1. Среднее значение метрики заметно больше медианы. О чём это говорит?
AВ данных ошибка: среднее не может быть больше медианы
BРаспределение скошено вправо — есть длинный хвост больших значений
CРаспределение симметрично
DВ выборке слишком мало наблюдений
2. Почему для времени ответа сервиса используют p95 и p99, а не среднее?
AПерцентили считаются быстрее среднего
BСреднее прячет тяжёлый хвост: при среднем 200 мс каждый сотый пользователь может ждать 8 секунд
CСреднее нельзя посчитать для времени
Dp99 всегда равен максимуму
3. Почему выручку нельзя оценить как «медианный чек × число чеков»?
AПотому что медиана всегда меньше среднего
BПотому что медиана не аддитивна: сумма всех значений восстанавливается только через среднее
CПотому что число чеков — целое, а медиана дробная
DМожно, это стандартный приём