Описательная статистика: среднее врёт чаще, чем кажется
Вы выгрузили таблицу на два миллиона строк. В отчёт войдёт одно число. Между этими двумя фактами лежит операция, которую редко проговаривают вслух: сжатие с потерями. Описательная статистика — это кодек, и вопрос не в том, работает ли он, а в том, что именно он выбросил.
Типичный провал: аналитик показывает «средний чек 4 300 рублей, растёт четвёртый месяц». Через квартал выясняется, что медианный чек падает, а среднее тянут вверх пять корпоративных заказов в месяц. Розничный сегмент деградирует, продукт оптимизировали не туда. Данные не врали — врало сжатие.
Дальше: шесть мер центра вместо одной, меры разброса, квантили и их несовместимые определения, форма распределения, робастность к выбросам — и сквозная тема трека: что ваш способ сбора данных вообще позволяет описывать. Аппарат вероятности разобран в математике, откуда берутся данные — в https://courses.digitable.life/post/data-analytics/02-data-sources/ и https://courses.digitable.life/post/data-analytics/03-data-quality/. И правило, которое стоит принять до всех формул: любая описательная статистика показывается вместе с числом наблюдений и мерой разброса; одинокое среднее — не факт, а утверждение без доказательства.
Меры центра: почему их шесть
$$\bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i$$
Среднее арифметическое — точка баланса выборки, физический центр тяжести. Его незаменимое
свойство: оно суммируемо. Зная среднее и n, вы знаете сумму — выручку, суммарное время,
трафик; «средний чек × число заказов = выручка» работает, «медианный чек × число заказов» не
означает ничего. Фатальный недостаток: точка баланса не обязана находиться там, где есть данные.
Медиана делит упорядоченную выборку пополам и отвечает на другой вопрос — «что у типичного?». Она не суммируема, зато чтобы её сдвинуть, нужно испортить половину данных.
Мода — самое частое значение: для непрерывных величин почти бесполезна, для категориальных — единственная определённая мера центра («средний способ оплаты» не существует, самый частый существует). Мода критична там, где данные кучкуются: «1,7 товара в корзине» скрывает, что в 70 % корзин ровно один товар.
Усечённое (trimmed) среднее отбрасывает по k % с каждого края, винзоризованное
(winsorized) заменяет края на границу, сохраняя n. Оба приёма обязаны быть в подписи:
«средний чек, усечение 5 %» — честно, тихо выкинутый хвост — подлог.
Геометрическое среднее — для темпов роста:
$$G = \left(\prod_{i=1}^{n} x_i\right)^{1/n}$$
MAU по месяцам: +50 %, −40 %, +50 %, −40 %. Арифметическое среднее темпов даёт (50 − 40 + 50 − 40) / 4 = +5 % в месяц. Реальность: 1,5 · 0,6 · 1,5 · 0,6 = 0,81, то есть −19 % за четыре месяца. Геометрическое среднее множителей — 0,81 в степени 1/4 ≈ 0,9487, или −5,13 % в месяц, и оно воспроизводит итог: 0,9487 в четвёртой степени = 0,81. Темпы роста, доходности и коэффициенты усредняются геометрически.
Гармоническое среднее — для «единиц на что-то»:
$$H = \frac{n}{\sum_{i=1}^{n} 1/x_i}$$
Две кампании с бюджетом по 1 000 USD, стоимость привлечения 50 USD и 200 USD. Арифметика даёт 125 USD. Реальность: 20 + 5 = 25 конверсий на 2 000 USD = 80 USD, и ровно это даёт гармоническое среднее: 2 / (1/50 + 1/200) = 80. Если у слагаемых общий знаменатель — одинаковый бюджет, расстояние, время — усредняем гармонически.
Взвешенное среднее (сумма произведений «значение × вес», делённая на сумму весов) — против
ошибки «среднее средних». Магазин A: 100 заказов по 1 000 рублей, магазин B: 900 заказов по
3 000 рублей. «Среднее средних» = 2 000, взвешенное = (100 · 1 000 + 900 · 3 000) / 1 000 =
2 800. Ошибка на 40 % возникает каждый раз, когда агрегат считается по уже агрегированной
витрине: AVG(avg_check) вместо SUM(revenue) / SUM(orders). Тот же корень — у проблемы с CTR.
среднее только рядом с ними"] G -- нет --> I{"Есть выбросы или ошибки ввода?"} I -- да --> J["Усечённое или винзоризованное среднее"] I -- нет --> K["Среднее арифметическое"] C & E & F & H & J & K --> L["Обязательно рядом: n и мера разброса"]
Разбор на десяти числах
Отношение mean / median — дешёвый детектор скошенности; вот на чём видно, зачем он нужен.
Зарплаты в отделе, тысячи рублей: 60, 65, 70, 70, 75, 80, 85, 90, 110, 900, где последнее —
партнёр, числящийся в отделе формально.
| Мера | Значение | Комментарий |
|---|---|---|
| Среднее | 160,5 | 9 из 10 человек получают меньше среднего |
| Медиана | 77,5 | среднее 5-го и 6-го значений |
mean / median |
2,07 | больше 1,2 — сигнал тревоги |
Выборочное СКО s |
260,2 | больше самого среднего; CV = 1,62 |
| Q1 / Q3 / IQR | 70 / 88,75 / 18,75 | |
| MAD | 10 | медиана модулей отклонений от медианы |
| Робастное СКО (1,4826 · MAD) | 14,8 | в 17 раз меньше обычного |
| Усечённое / винзоризованное среднее (10 %) | 80,6 / 82,0 |
Расчёт s: сумма квадратов отклонений от среднего = 609 472,5; делим на n − 1 = 9, получаем
67 719,17; корень — 260,23. Теперь проверим наблюдение 900 на выброс двумя способами:
- Правило 1,5 · IQR: граница Q3 + 1,5 · IQR = 88,75 + 28,125 = 116,875. Выброс. Работает.
- Правило трёх сигм: граница 160,5 + 3 · 260,23 = 941,2. «В пределах нормы». Не работает.
Это системное свойство, а не курьёз: выброс входит в расчёт среднего и СКО и раздувает их настолько, что маскирует сам себя — чем он крупнее, тем надёжнее прячется. Отсюда рекомендация Leys et al. (2013): искать выбросы робастным z-показателем с порогом 3 по модулю,
$$z_i = \frac{x_i - \mathrm{median}(x)}{1{,}4826 \cdot \mathrm{MAD}}$$
для нашего наблюдения он равен (900 − 77,5) / 14,83 ≈ 55,5 — не заметить нельзя. Коэффициент 1,4826 приводит MAD к масштабу СКО для нормального распределения, для остальных это лишь нормировка.
import numpy as np
s = np.array([60, 65, 70, 70, 75, 80, 85, 90, 110, 900], dtype=float)
q1, q3 = np.percentile(s, [25, 75]) # 70.0, 88.75 — интерполяция, тип 7
print(q3 + 1.5 * (q3 - q1)) # 116.875 — правило IQR ловит выброс
print(s.mean() + 3 * s.std(ddof=1)) # 941.19 — правило трёх сигм пропускает
print(np.median(np.abs(s - np.median(s)))) # 10.0 — MAD
print((s < s.mean()).mean()) # 0.9 — доля тех, кто ниже "среднего"
Разброс: что показывать рядом с центром
$$s^2 = \frac{1}{n-1}\sum_{i=1}^{n}(x_i - \bar{x})^2$$
Деление на n − 1 — поправка Бесселя: отклонения считаются не от истинного среднего
совокупности, а от выборочного, подогнанного под данные, поэтому сумма квадратов систематически
занижена и делитель уменьшают. В SQL это stddev_samp / var_samp против stddev_pop /
var_pop; если у вас вся совокупность, корректнее pop. Беда СКО та же, что у среднего:
квадрат отклонения даёт выбросам огромный вес, и на тяжёлых хвостах — а в продуктовых данных они
везде — СКО перестаёт нести смысл.
| Мера | Определение | Ломается на выбросах | Единицы |
|---|---|---|---|
| Размах | max − min | катастрофически | как у данных |
| Дисперсия и СКО | средний квадрат отклонения и его корень | сильно | квадрат единиц и единицы |
| Коэффициент вариации | s / x̄ |
сильно | безразмерный |
| IQR | Q3 − Q1 | нет | как у данных |
| MAD | медиана модулей отклонений от медианы | нет | как у данных |
| p90 − p10 | межквантильный разброс | почти нет | как у данных |
Коэффициент вариации полезен ровно в одном сценарии — сравнить разброс величин в разных единицах (латентность против чека); он бессмыслен для величин, проходящих через ноль.
Сколько наблюдений стоит за цифрой
«Конверсия в стране X — 40 %» звучит одинаково при 4 визитах из 10 и при 40 000 из 100 000. Стандартная ошибка среднего:
$$SE = \frac{s}{\sqrt{n}}$$
Для нашего отдела: 260,23 / √10 ≈ 82,3, то есть «средняя зарплата 160,5» означает нечто вроде
«где-то между 0 и 320»; как превратить это в честный интервал — https://courses.digitable.life/post/data-analytics/07-uncertainty/.
Гигиена прямо сейчас: n колонкой в каждой строке отчёта, а не в тултипе; группы с n меньше
порога (30 — разумная отправная точка) сворачиваются в «Прочее»; число уникальных сущностей
показывается рядом с числом строк — 10 000 событий от 3 пользователей это не то же, что от 8 000.
Квантили: как их на самом деле считают
Квантиль уровня p — значение, ниже которого лежит доля p наблюдений. Для конечной выборки
определение неоднозначно, и инструменты разрешают неоднозначность по-разному: Hyndman и Fan
(1996) насчитали девять употребимых определений; в SQL вы встретите два.
Ближайший ранг (percentile_disc) возвращает реально существующее значение:
$Q(p) = x_{\lceil np \rceil}$. Линейная интерполяция (percentile_cont,
numpy.percentile, quantile в R) для массива с индексами от 1 до n:
$$h = (n-1)p, \qquad Q(p) = x_{\lfloor h \rfloor + 1} + (h - \lfloor h \rfloor)\left(x_{\lfloor h \rfloor + 2} - x_{\lfloor h \rfloor + 1}\right)$$
На выборке 10, 20, 30, 40, 100 при p = 0,9: percentile_disc даёт ранг ⌈4,5⌉ = 5 и значение
100; percentile_cont даёт h = 3,6 и 40 + 0,6 · (100 − 40) = 76. Два корректных ответа
отличаются на 30 %. Если дашборд и ноутбук показывают разные p90, сравнивайте определения, а не
ищите ошибку в данных; для денег и SLA обычно лучше percentile_disc.
Процентили не складываются и не усредняются
Два сервера, по 1 000 запросов. A: 960 запросов по 100 мс, 40 по 900 мс, p95 по ближайшему рангу (950-е значение) = 100 мс. B: 900 по 100 мс, 100 по 900 мс, p95 = 900 мс. Среднее двух p95 = 500 мс. Настоящий p95 по всем 2 000 запросам: быстрых 1 860, медленных 140, ранг ⌈0,95 · 2000⌉ = 1900 — за пределами быстрой части, значит p95 = 900 мс, вдвое хуже «среднего».
Из p95 по частям нельзя получить p95 по целому — ни средним, ни взвешенным, ни максимумом. Нужны
либо сырые наблюдения, либо мержабельные скетчи: t-digest (Ted Dunning; в ClickHouse —
quantileTDigest с комбинаторами -State / -Merge) или HdrHistogram (Gil Tene). Храните в
витрине не число p95, а состояние скетча — тогда любой разрез пересчитается корректно
(https://courses.digitable.life/post/data-analytics/04-sql-for-analysis/,
колоночные хранилища).
Чего не видно изнутри данных
Идеально посчитанный p99 может описывать не то, что вы думаете. Coordinated omission (разбор Gil Tene): нагрузочный клиент шлёт запрос, ждёт ответа и только потом шлёт следующий; когда сервис тормозит, клиент сам снижает нагрузку — и худшие интервалы не попадают в выборку. Измеренный p99 в разы оптимистичнее того, что чувствует пользователь: это ограничение схемы сбора, а не ошибка расчёта. Семплирование искажает так же избирательно — среднее по 1 % данных оценивается прилично, а p99 и максимум плохо, потому что хвост состоит из редких событий и выкашивается первым. Правило: агрегаты центра терпят семплирование, агрегаты хвоста — нет.
Форма: то, что сводка не показывает
Коэффициент асимметрии (третья степень отклонений) для решений почти не используют — он ещё менее
устойчив, чем СКО. Полезнее два эмпирических индикатора: mean / median (больше 1,2 — правый
хвост, меньше 0,85 — левый, что редко, но бывает: остаток на счёте перед списанием) и
(p90 − p50) / (p50 − p10) — значение 3 и выше означает, что средним пользоваться нельзя.
Модальность численно почти не ловится и почти всегда означает, что вы смешали две популяции: бимодальная латентность — кэш-хит и кэш-мисс, бимодальное время доставки — самовывоз и курьер, бимодальный чек — физлица и юрлица. Правильная реакция — не искать хитрую меру центра, а разделить сегменты; тот же принцип работает в https://courses.digitable.life/post/data-analytics/11-cohorts-and-retention/.
Ящик с усами: полезен, но не всесилен
Ящик с усами (Tukey, 1977) кодирует пятичисловую сводку и рисует точками всё, что вышло за
границы Q1 − 1,5 · IQR и Q3 + 1,5 · IQR. Это лучший способ сравнить много групп на одном
экране: двадцать боксплотов рядом читаются, двадцать гистограмм — нет. Чего он не показывает:
n (ящик по 5 наблюдениям выглядит так же солидно, как по 50 000) и модальность — нижняя
часть картинки это два набора по 40 значений с совпадающей до знака сводкой и разной формой.
Лечится дёшево: точки поверх ящика (jitter, beeswarm) или подпись n у групп. О том, как
график врёт честными данными, — https://courses.digitable.life/post/data-analytics/12-visualization/.
Тот же урок в двух знаменитых работах. Анскомб (1973) построил четыре набора по 11 точек с
идентичными статистиками (среднее x = 9, дисперсия x = 11, среднее y = 7,50, дисперсия
y ≈ 4,125, корреляция ≈ 0,816, регрессия y = 3,00 + 0,500·x), где первый — облако, второй —
парабола, третий — прямая с выбросом, четвёртый — столбец плюс одна точка, создающая всю
корреляцию. Матейка и Фицморис (2017) довели идею до предела: их Datasaurus Dozen — дюжина
наборов с совпадающими до двух знаков статистиками, один из которых рисует динозавра.
Агрегация, которая меняет знак: парадокс Симпсона
| Канал | Платформа | Визиты | Конверсии | CR |
|---|---|---|---|---|
| A | десктоп | 200 | 40 | 20,0 % |
| A | мобайл | 800 | 80 | 10,0 % |
| B | десктоп | 800 | 144 | 18,0 % |
| B | мобайл | 200 | 18 | 9,0 % |
Итоги: A — 120 конверсий на 1000 визитов (12,0 %), B — 162 на 1000 (16,2 %). В каждом сегменте лучше канал A, в сумме лучше канал B, и оба утверждения арифметически верны. Причина — разный состав трафика: десктоп конвертирует лучше, и B получает 80 % десктопа, а A только 20 %. Платформа здесь конфаундер: влияет и на выбор канала, и на конверсию.
Какое число правильное — зависит от вопроса, и это не отговорка. «Куда переложить бюджет при неизменном микс-трафике?» — смотрите разрез, побеждает A. «Какой канал сейчас приносит больше конверсий на визит?» — смотрите итог, побеждает B. «Что будет, если увеличить бюджет на A?» — на это описательная статистика не отвечает вообще: https://courses.digitable.life/post/data-analytics/10-causality/.
-- GROUPING SETS даёт разрез и итог одним запросом. Сравнивать их — обязательная гигиена.
SELECT
channel,
coalesce(platform, 'ВСЕ ПЛАТФОРМЫ') AS platform,
sum(visits) AS visits,
sum(conversions) AS conversions,
round(100.0 * sum(conversions) / nullif(sum(visits), 0), 2) AS cr_pct
FROM channel_daily
WHERE day >= date '2026-06-01'
GROUP BY GROUPING SETS ((channel, platform), (channel))
ORDER BY channel, platform;
Если знак разницы между каналами в итоговой строке не совпадает со знаком в разрезах — вы нашли конфаундер; не «поправьте цифру», а разберитесь, почему состав трафика разный. Разбор Bickel, Hammel и O’Connell (Science, 1975) о приёме в Беркли — тот же механизм: суммарно доля принятых женщин ниже, по факультетам — не ниже. Почему разрезы меняют картину системы, — в системном мышлении.
Выбросы: что с ними делать
Выброс не мусор по определению — иногда это самое ценное наблюдение: именно там ломается продукт.
- Никогда не удаляйте выброс молча. В отчёте пишем, сколько наблюдений исключено, по какому правилу и какой процент суммы они составляли. Если исключённые 0,1 % строк давали 30 % выручки — это и есть главный вывод отчёта.
- Правило исключения живёт в пайплайне, а не в ноутбуке, иначе цифра не воспроизводится: https://courses.digitable.life/post/data-analytics/03-data-quality/, качество данных.
- Отделяйте невозможное от редкого: возраст 150 лет — ошибка схемы, заказ на миллион — нет.
Что позволяет ваш способ сбора данных
Описательная статистика описывает не реальность, а ваш датасет. Между ними стоит процедура сбора, и она умеет систематически искажать любую меру центра.
Цензурирование справа. «Средняя длительность сессии» считается по строкам с заполненным
ended_at, но незавершённые сессии — как раз самые длинные, и среднее систематически занижено.
Правильно: считать на закрытом окне, где все сессии завершились, либо переходить к анализу
выживаемости.
Отбор по выжившим. «Средний срок жизни клиента — 8 месяцев», посчитанный по ушедшим, игнорирует всех, кто с вами два года: это не оценка LTV, а оценка «сколько живут те, кто уже умер».
Тихая фильтрация статусом. WHERE status = 'paid' разумно для выручки и катастрофично для
«среднего чека попытки»: каждое условие в WHERE меняет популяцию, о которой вы делаете
утверждение, — формулируйте её вслух. Сюда же разное разрешение измерения: клиент округляет
длительность до секунд, сервер пишет миллисекунды, приложение шлёт события пачками при выходе из
фона, а веб — сразу, и среднее по объединению описывает смесь двух измерительных процедур.
-- Показываем размер лжи явно: open_pct — доля сессий, которых нет в расчёте среднего.
SELECT
count(*) AS sessions_total,
round(100.0 * count(*) FILTER (WHERE ended_at IS NULL) / count(*), 1) AS open_pct,
round(avg(extract(epoch FROM (ended_at - started_at)))::numeric, 1) AS mean_sec_closed
FROM sessions
WHERE started_at >= date '2026-07-01';
Перед расчётом ответьте письменно на три вопроса: кто попал в эти данные, кто не попал и в какой момент значение зафиксировано. Ответ должен войти в подпись к цифре — та же дисциплина, что в https://courses.digitable.life/post/data-analytics/01-question-first/ и системном анализе.
SQL-практикум
Базовая сводка вместо одинокого среднего — то, что должно уходить в отчёт по умолчанию. Колонка
mean_to_median и есть детектор: если она стабильно выше 1,2, все графики «среднего» надо
заменить на медиану с квантилями.
-- PostgreSQL
SELECT
date_trunc('month', created_at) AS month,
count(*) AS orders,
count(DISTINCT user_id) AS buyers,
round(avg(amount_usd)::numeric, 2) AS mean_amount,
round(percentile_cont(0.5) WITHIN GROUP (ORDER BY amount_usd)::numeric, 2) AS p50,
round(percentile_cont(0.9) WITHIN GROUP (ORDER BY amount_usd)::numeric, 2) AS p90,
round(stddev_samp(amount_usd)::numeric, 2) AS sd,
round((avg(amount_usd)
/ nullif(percentile_cont(0.5) WITHIN GROUP (ORDER BY amount_usd), 0))::numeric, 2)
AS mean_to_median
FROM orders
WHERE status = 'paid' AND created_at >= date '2026-01-01'
GROUP BY 1
ORDER BY 1;
Отношение сумм против среднего отношений — самая дорогая ошибка продуктовой аналитики:
WITH per_user AS (
SELECT
user_id,
count(*) FILTER (WHERE event_type = 'click') AS clicks,
count(*) FILTER (WHERE event_type = 'impression') AS impressions
FROM events
WHERE event_time >= date '2026-06-01'
GROUP BY user_id
)
SELECT
-- НЕВЕРНО: пользователь с одним показом весит как пользователь с тысячей
round(avg(clicks::numeric / nullif(impressions, 0)) * 100, 3) AS avg_of_ratios_pct,
-- ВЕРНО: вес пропорционален числу показов
round(sum(clicks)::numeric / nullif(sum(impressions), 0) * 100, 3) AS ratio_of_sums_pct,
count(*) FILTER (WHERE impressions = 0) AS users_without_impressions
FROM per_user;
Масштаб расхождения: пользователь A с одним показом и одним кликом даёт CTR 100 %, пользователь B
с 999 показами и 10 кликами — 1,0 %. Среднее отношений = 50,5 %, отношение сумм = 11 / 1000 =
1,1 %. Разница в 46 раз. Плюс avg молча пропускает NULL, то есть пользователей без
показов: у «неверной» цифры другой знаменатель — поэтому последняя колонка обязательна.
Робастная сводка одним запросом; если sd_classic в разы больше sd_robust, в данных хвост или
мусор:
WITH med AS (
SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY amount_usd) AS m
FROM orders WHERE status = 'paid'
)
SELECT
round(med.m::numeric, 2) AS median_amount,
round((1.4826 * percentile_cont(0.5) WITHIN GROUP (
ORDER BY abs(o.amount_usd - med.m)))::numeric, 2) AS sd_robust,
round(stddev_samp(o.amount_usd)::numeric, 2) AS sd_classic
FROM orders o CROSS JOIN med
WHERE o.status = 'paid'
GROUP BY med.m;
И корректный p95 за месяц из посуточной витрины — единственный способ, которым это вообще можно
сделать (SELECT avg(p95_ms) FROM daily_latency даст неверный ответ всегда):
-- ClickHouse; q_state: AggregateFunction(quantileTDigest, UInt32), пишется как
-- quantileTDigestState(latency_ms) при вставке в AggregatingMergeTree.
SELECT route, quantileTDigestMerge(0.95)(q_state) AS p95_ms
FROM latency_daily_state WHERE day >= today() - 30
GROUP BY route ORDER BY p95_ms DESC;
Типичные ошибки
- Одинокое среднее без
nи разброса — не число, а мнение. AVGот уже усреднённой колонки и среднее отношений вместо отношения сумм в CTR, конверсии, доле ошибок: считайтеSUM / SUM.- Усреднение процентилей по дням, серверам, шардам; сравнение цифр, посчитанных разными определениями квантилей — дашборд против ноутбука.
- Арифметическое среднее темпов роста вместо геометрического.
- Правило трёх сигм для поиска выбросов — выброс маскирует сам себя.
- Тихое удаление хвоста без строки «исключено N наблюдений, M % суммы»; игнорирование цензурирования в длительностях, сроках жизни, времени до события.
- Одна цифра на бимодальные данные — сначала разделите популяции.
- Среднее по порядковой шкале: «средняя удовлетворённость 3,7» — арифметика над метками; показывайте доли, как в UX-метриках.
Чек-лист перед тем, как показать цифру
- Написано, какая популяция попала в расчёт и кто отфильтрован; рядом есть
nи число уникальных сущностей. - Посчитано
mean / median; если больше 1,2 — показана медиана и квантили. - Построена гистограмма, проверено на две моды; глазами просмотрены 10 максимальных и 10 минимальных значений.
- Отношения посчитаны как
SUM / SUM, процентили — из сырых данных или мержабельных состояний; агрегат проверен разрезом хотя бы по одному конфаундеру. - Правило исключения выбросов зафиксировано в коде пайплайна и в подписи.
- Есть ответ на вопрос «какое решение изменится, если цифра будет другой».
Последний пункт — не про статистику, но именно он отличает отчёт, который читают, от дашборда, который открывают один раз: https://courses.digitable.life/post/data-analytics/13-dashboards/, https://courses.digitable.life/post/data-analytics/14-metrics-and-goodhart/.
Мини-итог
- Среднее суммируемо и незаменимо для итогов, но беззащитно перед одним большим наблюдением;
медиана устойчива, но не суммируется. Показывайте обе, рядом с
nи разбросом. - Темпы роста усредняются геометрически, цены за единицу при общем знаменателе — гармонически, агрегаты по группам — с весами. СКО и правило трёх сигм заменяются на IQR и MAD.
- Квантили считаются по-разному в разных инструментах, не складываются и не усредняются: для витрин храните t-digest или HdrHistogram, а не готовые числа.
- Совпадение сводных статистик не гарантирует ничего (Anscombe, Datasaurus), а любая доля проверяется разрезом — Симпсон умеет менять знак вывода.
- Цифра описывает не мир, а вашу процедуру сбора: цензурирование, отбор по выжившим, семплирование и coordinated omission искажают статистики систематически — так, что изнутри данных этого не видно.
Источники
- Anscombe, F. J. Graphs in Statistical Analysis. The American Statistician, 27(1), 1973. https://www.jstor.org/stable/2682899
- Matejka, J., Fitzmaurice, G. Same Stats, Different Graphs (Datasaurus Dozen), CHI 2017. https://www.autodesk.com/research/publications/same-stats-different-graphs
- Hyndman, R. J., Fan, Y. Sample Quantiles in Statistical Packages. The American Statistician, 50(4), 1996. https://robjhyndman.com/publications/quantiles/
- Tukey, J. W. Exploratory Data Analysis. Addison-Wesley, 1977. Leys, C. et al. Detecting outliers: Do not use standard deviation around the mean, use absolute deviation around the median. JESP, 49(4), 2013. https://doi.org/10.1016/j.jesp.2013.03.013
- Bickel, P. J., Hammel, E. A., O’Connell, J. W. Sex Bias in Graduate Admissions: Data from Berkeley. Science, 187(4175), 1975. https://www.science.org/doi/10.1126/science.187.4175.398
- Dunning, T. Computing Extremely Accurate Quantiles Using t-Digests https://github.com/tdunning/t-digest; Tene, G. How NOT to Measure Latency https://www.infoq.com/presentations/latency-response-time/, http://hdrhistogram.org/
- Документация: PostgreSQL Aggregate Functions https://www.postgresql.org/docs/current/functions-aggregate.html, ClickHouse quantile functions https://clickhouse.com/docs/sql-reference/aggregate-functions/reference/quantile
Что дальше
Мы научились честно описывать центр и разброс — и несколько раз упёрлись в одно и то же: как только у распределения появляется тяжёлый хвост, привычные меры теряют смысл, а иногда перестают существовать вовсе. Именно в хвосте живут инциденты, крупные клиенты и дорогие решения.