Exploratory Data Analysis: распределения и аномалии
Содержание курса
2. Форма распределения: что скрывается за числами
Описательная статистика отвечает на вопрос «где» и «насколько широко» — но не отвечает на вопрос «как именно» распределены значения. Два признака могут иметь одинаковое среднее и стандартное отклонение, но совершенно разную форму. Форма распределения характеризуется несколькими вещами:
- Симметрия — значения равномерно расположены вокруг центра, как у нормального распределения.
- Скошенность (skew) — хвост тянется в одну сторону. Правый хвост означает, что большинство значений небольшие, но есть редкие очень крупные.
- Моды — количество пиков. Унимодальное распределение имеет один пик, бимодальное — два, что часто сигнализирует о двух разных группах в данных.
- Хвосты — насколько далеко уходят крайние значения от центра. В
rossmann_train.csvэто хорошо видно на примереSales. Разница между 25-м перцентилем (3 727) и 75-м (7 856) составляет около 4 100. При этом максимум — 41 551, то есть правый хвост простирается почти в 9 раз дальше верхнего квартиля. Это классическое правостороннее распределение: большинство магазинов дают выручку в диапазоне 3 700–7 900, но есть единицы с продажами на порядок выше. Посмотреть форму числом можно черезskew()иkurt():
import pandas as pd
df = pd.read_csv('rossmann_train.csv')
print(df[['Sales', 'Customers']].skew())
print(df[['Sales', 'Customers']].kurt())
Значение skew > 0 означает правый хвост, skew < 0 — левый. Для Sales и Customers ожидаем заметный положительный skew, что подтверждает визуальное наблюдение из describe(). Почему форма важна на практике? Линейные модели предполагают определённые свойства остатков; если целевая переменная сильно скошена, логарифмическое преобразование Sales перед обучением может существенно улучшить качество. Кроме того, бимодальность часто указывает на смешение подгрупп — например, магазины разного типа или рабочие и выходные дни. В rossmann_train.csv дни, когда магазин закрыт (Open=0), дают Sales=0, что формирует отдельный пик в нуле и делает распределение Sales без фильтрации смесью двух режимов. Поэтому перед анализом формы распределения полезно сначала отфильтровать строки с Open=1:
df_open = df[df['Open'] == 1]
print(df_open[['Sales', 'Customers']].skew())
Это даст честную картину распределения выручки в рабочие дни, без артефакта от нулей.
