Разброс данных — это то, насколько сильно значения отклоняются от своего среднего. Дисперсия и стандартное отклонение — два способа выразить этот разброс одним числом. Дисперсия — это средний квадрат отклонения от среднего. Стандартное отклонение — корень из дисперсии, взятый ровно для того, чтобы вернуться к исходным единицам измерения. Если запомнить только это, вы уже не запутаетесь. Всё остальное — нюансы, которые я разберу ниже на живых примерах.
Я постоянно натыкаюсь на аналитиков, которые считают только среднее и на этом останавливаются. А среднее без разброса врёт. Два дня продаж с одинаковым средним чеком 1000 рублей могут выглядеть совершенно по-разному: в один день все заказы кучкуются вокруг тысячи, а в другой половина по 200, половина по 5000. Среднее одно, а бизнес — разный. Разброс как раз ловит эту разницу.
Что такое разброс данных и зачем он аналитику?
Представьте два отдела с одинаковой средней зарплатой. В первом все получают примерно поровну, во втором есть пара топов и толпа стажёров. Средняя совпадает, ощущения от работы — противоположные. Разброс — это про то, насколько значения расползаются вокруг центра.
Самая наивная попытка измерить разброс — взять отклонения каждого значения от среднего и усреднить их. Не работает: отклонения выше среднего положительные, ниже — отрицательные, в сумме они гасят друг друга и дают ноль. Всегда. Поэтому от знаков нужно избавиться.
Можно было бы брать модуль отклонения (это называется среднее абсолютное отклонение), но математически удобнее возводить отклонения в квадрат. Квадрат убивает знак и заодно сильнее наказывает большие отклонения — выброс в 10 раз дальше среднего даёт вклад в 100 раз больший. Именно на квадратах и строится дисперсия.
Как считается дисперсия по шагам?
Возьмём пять заказов: 200, 400, 1000, 1400, 5000. Считаем среднее: сумма 8000, делим на 5, получаем 1600. Теперь по шагам:
- отклонения от среднего: -1400, -1200, -600, -200, 3400;
- квадраты отклонений: 1 960 000, 1 440 000, 360 000, 40 000, 11 560 000;
- сумма квадратов: 15 360 000.
Дальше эту сумму нужно на что-то поделить, чтобы получить «средний квадрат отклонения». И вот тут начинается главная развилка: делить на количество значений или на количество минус один. Об этом отдельная глава ниже, а пока просто зафиксируем: дисперсия — это усреднённая сумма квадратов отклонений. Число получилось огромным и в «рублях в квадрате» — величине, которую невозможно себе представить. Поэтому дисперсию почти никогда не показывают в отчётах в чистом виде.
Чем дисперсия отличается от стандартного отклонения?
Единицами измерения — и это единственное принципиальное различие. Дисперсия живёт в квадрате исходных единиц: если данные в рублях, дисперсия в рублях в квадрате, если в секундах — в секундах в квадрате. Такую величину нельзя ни с чем сравнить и невозможно интуитивно осмыслить.
Стандартное отклонение — это корень из дисперсии. Корень возвращает нас обратно в рубли, секунды, штуки. Поэтому в жизни аналитик почти всегда оперирует стандартным отклонением, а дисперсия остаётся промежуточным шагом и удобной величиной для формул.
Практический смысл прост. Если средний чек 1600 рублей, а стандартное отклонение 1800 рублей — данные разбросаны шире, чем само среднее, распределение «рваное», одним средним описывать бессмысленно. Если же стандартное отклонение 150 рублей при среднем 1600 — почти все заказы плотно сидят рядом со средним, и им можно доверять как типичным. Одно число сразу говорит, насколько устойчива ваша средняя.
Почему при расчёте дисперсии делят на n-1?
Потому что обычно у нас на руках не вся генеральная совокупность, а только выборка, и деление на n систематически занижает разброс. Деление на n-1 (поправка Бесселя) это компенсирует. Разберём, откуда берётся занижение.
Мы считаем отклонения не от истинного среднего всей популяции — его мы не знаем — а от среднего самой выборки. А среднее выборки по построению сидит ровно в центре этих же данных, оно к ним «подогнано». От своего же среднего точки всегда отклоняются чуть меньше, чем от настоящего среднего популяции. Значит, сумма квадратов выходит чуть меньше, чем должна быть, и деление на n даёт смещённую вниз оценку.
Деление на n-1 вместо n увеличивает результат и убирает это смещение. Формально говорят, что мы «потратили одну степень свободы» на оценку среднего. Чем меньше выборка, тем сильнее разница: на 5 точках делить на 4 вместо 5 — это плюс 25 процентов к дисперсии, ощутимо. На 100 000 строк разница между n и n-1 исчезает, и спорить не о чем.
Практическое правило: если у вас выборка и вы хотите оценить разброс всей популяции — делите на n-1 (это называется выборочная дисперсия). Если ваши данные и есть вся популяция целиком — можно делить на n (популяционная дисперсия). В 95 процентах задач аналитика работает с выборкой, поэтому n-1 — вариант по умолчанию. Хорошая новость: pandas и большинство SQL-движков по умолчанию и так считают выборочный вариант, но об этом ниже.
Как стандартное отклонение помогает искать выбросы?
Стандартное отклонение задаёт естественный масштаб «нормального» расстояния от среднего. Выброс — это значение, которое отстоит от среднего на слишком много таких стандартных шагов.
Обычный практический порог — три стандартных отклонения. Значение, которое дальше трёх сигм от среднего, считают кандидатом в выбросы и идут разбираться: то ли это ошибка данных, то ли настоящий, но редкий случай. Порог не священный — иногда берут два, иногда четыре, зависит от того, насколько дорого пропустить аномалию.
Тут важная оговорка: и среднее, и стандартное отклонение сами по себе чувствительны к выбросам. Один заказ на миллион раздует и среднее, и сигму, и после этого «правило трёх сигм» может вообще ничего не поймать, потому что порог уедет далеко вверх. Поэтому на грязных данных выбросы часто ищут через медиану и межквартильный размах (IQR) — они устойчивее. Держите оба инструмента в голове и выбирайте по ситуации. Потренироваться на реальных грязных данных удобно в Python-тренажёре, там есть задачи ровно про чистку и аномалии.
Что такое z-оценка и как её посчитать?
Z-оценка (или стандартизованное значение) отвечает на вопрос: на сколько стандартных отклонений данное значение отстоит от среднего. Считается элементарно — берём отклонение от среднего и делим на стандартное отклонение: z = (значение - среднее) / стандартное_отклонение.
Z-оценка обезразмеривает данные. Заказ на 5000 рублей и время сессии в 400 секунд напрямую не сравнить — разные единицы, разные масштабы. Но их z-оценки сравнить можно: обе показывают, «насколько это значение необычно для своего распределения». Заказ с z равным 3.4 так же экстремален внутри чеков, как сессия с z равным 3.4 внутри времён.
Отсюда два повседневных применения. Первое — поиск выбросов: фильтруем строки, где модуль z больше 3. Второе — приведение разных признаков к общему масштабу перед моделями и кластеризацией, чтобы признак с большими числами не «перекрикивал» остальные. Это стандартизация, и она встречается на каждом шагу в ML.
Что даёт на практике правило 68-95-99.7?
Оно даёт быструю прикидку, какая доля данных попадает в окрестность среднего, если распределение примерно нормальное (колоколообразное). Правило звучит так: около 68 процентов значений лежат в пределах одного стандартного отклонения от среднего, около 95 процентов — в пределах двух, и около 99.7 процента — в пределах трёх.
Как этим пользоваться на глаз. Пусть время загрузки страницы в среднем 800 мс со стандартным отклонением 200 мс, и оно распределено более-менее нормально. Тогда примерно 95 процентов пользователей видят загрузку в диапазоне от 400 до 1200 мс (среднее плюс-минус две сигмы). А загрузка в 1500 мс — это уже больше трёх сигм от среднего, то есть попадает в те самые редкие 0.3 процента хвоста. Отсюда же растёт порог «трёх сигм» для выбросов: за тремя сигмами в нормальном распределении почти ничего не должно быть.
Оговорка обязательна: правило работает только для распределений, близких к нормальному. Многие реальные метрики аналитика — чеки, выручка, длительности — скошены вправо, с длинным хвостом дорогих значений. Для них правило 68-95-99.7 завышает симметричность, и слепо применять его нельзя. Сначала посмотрите на гистограмму. Разобраться, где нормальность есть, а где нет, помогают конспекты по статистике и разбор реальных кейсов.
Как посчитать дисперсию и стандартное отклонение в SQL?
В SQL для этого есть готовые агрегатные функции, и главное — не перепутать выборочный вариант с популяционным. В PostgreSQL это STDDEV_SAMP и VAR_SAMP (деление на n-1) против STDDEV_POP и VAR_POP (деление на n). Просто STDDEV и VARIANCE в PostgreSQL — синонимы выборочных версий, то есть по умолчанию вы получаете деление на n-1, что обычно и нужно.
SELECT
AVG(amount) AS avg_amount,
VAR_SAMP(amount) AS variance, -- дисперсия, делит на n-1
STDDEV_SAMP(amount) AS std_dev -- стандартное отклонение
FROM payments
WHERE status = 'paid';
Разброс редко интересен «в целом» — обычно его смотрят в разрезе. Например, стабильность среднего чека по каждому дню: если в какой-то день сигма резко подскочила, там что-то произошло.
SELECT
date_trunc('day', created_at) AS day,
COUNT(*) AS orders,
ROUND(AVG(amount), 2) AS avg_amount,
ROUND(STDDEV_SAMP(amount), 2) AS std_dev
FROM payments
WHERE status = 'paid'
GROUP BY 1
ORDER BY 1;
Z-оценку тоже можно посчитать прямо в SQL через оконные функции — среднее и сигма по всей выборке, а потом отклонение каждой строки. Так удобно вытащить выбросы одним запросом:
SELECT user_id, amount, z_score
FROM (
SELECT
user_id,
amount,
(amount - AVG(amount) OVER ())
/ NULLIF(STDDEV_SAMP(amount) OVER (), 0) AS z_score
FROM payments
WHERE status = 'paid'
) t
WHERE ABS(z_score) > 3
ORDER BY ABS(z_score) DESC;
NULLIF(..., 0) тут страхует от деления на ноль, если вдруг все значения одинаковы и сигма равна нулю. Погонять эти запросы на настоящем PostgreSQL можно в SQL-тренажёре, а быстро освежить синтаксис агрегатов и оконных функций — в SQL-справочнике.
Как считать разброс в pandas?
В pandas всё в одну строку, но есть ловушка с параметром ddof, из-за которой pandas и numpy дают разные числа на одних и тех же данных. Метод .std() у pandas по умолчанию использует ddof=1, то есть делит на n-1 (выборочный вариант). А numpy.std() по умолчанию использует ddof=0, то есть делит на n. Об этом спотыкаются регулярно, в том числе на собеседованиях.
import pandas as pd
df = pd.read_csv('payments.csv')
df['amount'].mean() # среднее
df['amount'].var() # дисперсия, ddof=1 по умолчанию (делит на n-1)
df['amount'].std() # стандартное отклонение, тоже ddof=1
# явно популяционный вариант, если данные — вся совокупность
df['amount'].std(ddof=0) # делит на n
Z-оценка и фильтрация выбросов в pandas выглядят почти как их словесное описание:
mean = df['amount'].mean()
std = df['amount'].std()
df['z'] = (df['amount'] - mean) / std
outliers = df[df['z'].abs() > 3] # кандидаты в выбросы
clean = df[df['z'].abs() <= 3] # данные без экстремумов
А разброс в разрезе категорий — это groupby с несколькими агрегатами сразу. Например, посмотреть, у каких источников трафика самый нестабильный чек:
df.groupby('source')['amount'].agg(['mean', 'std', 'count'])
Смотреть на count рядом со std обязательно: на выборке из трёх строк стандартное отклонение — это шум, а не сигнал, и делать по нему выводы нельзя. Отработать эти приёмы на задачах можно в Python-тренажёре, а синтаксис agg и groupby всегда под рукой в Python-справочнике.
Что запомнить
Разброс — обязательный спутник среднего: без него средняя врёт. Дисперсия — средний квадрат отклонения от среднего, стандартное отклонение — корень из неё в исходных единицах, и в отчётах живёт именно оно. Делите на n-1, когда работаете с выборкой (а это почти всегда), — pandas и PostgreSQL по умолчанию так и делают, но numpy нет, держите это в голове. Z-оценка стандартизует значения и ловит выбросы за тремя сигмами, а правило 68-95-99.7 даёт быструю прикидку — но только на нормальных распределениях, которых среди метрик выручки меньше, чем хотелось бы.
Эти вопросы — про n-1, про разницу дисперсии и стандартного отклонения, про ddof в pandas — регулярно всплывают на собеседованиях аналитика. Потренироваться отвечать и решать смежные задачи можно в разделе вопросов с собеседований и на подборке задач. Если захочется системно и без пробелов пройти статистику вместе с SQL и Python, всё это открыто в Pro — без давления, просто когда почувствуете, что готовы двигаться глубже.