Распределение данных в машинном обучении
Узнайте, как работают распределения данных в Python: нормальное, скошенное и равномерное, как их обнаружить и почему они важны.
Распределение данных описывает, как значения распределяются по набору данных — какие значения встречаются часто, какие редко и насколько они отклоняются от центра. Прежде чем обучать любую модель машинного обучения, понимание распределения данных помогает выбрать правильный алгоритм, обнаружить аномалии, решить, нужно ли масштабировать или преобразовывать признаки, и избежать скрытых смещений в предсказаниях.
В этой главе рассматриваются наиболее распространённые формы распределений, способы их измерения в Python с помощью NumPy и SciPy, а также то, что каждое распределение означает для принятия решений при построении моделей.
Что такое распределение данных?
Распределение данных — это паттерн того, как часто каждое значение (или диапазон значений) встречается в наборе данных. При построении графика распределения вы обычно видите кривую или гистограмму, форма которой многое говорит об исходных данных.
Три свойства, определяющие любое распределение:
- Центр — место, где сосредоточена большая часть значений (измеряется средним значением, медианой и модой)
- Разброс — насколько сильно значения отклоняются от центра (измеряется стандартным отклонением и межквартильным размахом)
- Форма — является ли распределение симметричным, скошенным или плоским
Понимание всех трёх свойств необходимо перед тем, как подавать данные в модель.
Распространённые типы распределений
Нормальное распределение
Нормальное распределение (также называемое распределением Гаусса) — самое важное распределение в статистике и машинном обучении. Его гистограмма образует симметричную колоколообразную кривую: значения концентрируются вокруг среднего, а частота убывает одинаково с обеих сторон.
Ключевые свойства:
- Среднее значение, медиана и мода равны между собой.
- Приблизительно 68% значений попадают в пределы одного стандартного отклонения от среднего, 95% — в пределах двух, и 99,7% — в пределах трёх (эмпирическое правило).
- Полностью определяется двумя параметрами: средним (μ) и стандартным отклонением (σ).
Многие реальные явления приближаются к нормальному распределению — рост людей, ошибки измерений и показатели IQ являются классическими примерами. Алгоритмы, такие как линейный дискриминантный анализ, наивный байесовский классификатор Гаусса и линейная регрессия, предполагают (или выигрывают от) нормально распределённых признаков.
import numpy as np
from scipy import stats
# Simulate 1 000 adult heights (cm) drawn from a normal distribution
rng = np.random.default_rng(seed=42)
heights = rng.normal(loc=170, scale=10, size=1000)
print(f"Mean: {np.mean(heights):.1f} cm")
print(f"Std: {np.std(heights):.1f} cm")
# Verify the empirical rule
within_1_std = np.sum(np.abs(heights - 170) < 10) / 1000 * 100
within_2_std = np.sum(np.abs(heights - 170) < 20) / 1000 * 100
within_3_std = np.sum(np.abs(heights - 170) < 30) / 1000 * 100
print(f"Within 1 std: {within_1_std:.1f}% (expected ~68%)")
print(f"Within 2 std: {within_2_std:.1f}% (expected ~95%)")
print(f"Within 3 std: {within_3_std:.1f}% (expected ~99.7%)")Вывод:
Mean: 169.7 cm
Std: 9.9 cm
Within 1 std: 68.8% (expected ~68%)
Within 2 std: 95.7% (expected ~95%)
Within 3 std: 99.8% (expected ~99.7%)Скошенное распределение
Скошенное распределение является асимметричным: один «хвост» длиннее другого. Скошенность измеряется статистикой асимметрии — положительные значения указывают на правый (положительный) наклон, отрицательные — на левый (отрицательный), а значения, близкие к нулю, указывают на приближённую симметрию.
Положительно скошенное (правостороннее): хвост уходит вправо. Среднее значение оказывается выше медианы из-за небольшого количества очень больших значений. Типичные примеры — доходы и цены на жильё: несколько очень высокооплачиваемых людей или дорогих объектов недвижимости поднимают среднее значительно выше медианы.
Отрицательно скошенное (левостороннее): хвост уходит влево. Результаты экзаменов на лёгком тесте часто демонстрируют такой паттерн — большинство студентов получают высокие баллы, но несколько получают очень низкие.
import numpy as np
from scipy import stats
# Right-skewed: a small number of very high values pull the mean up
salaries = np.array([30000, 32000, 34000, 35000, 36000, 38000,
40000, 42000, 45000, 55000, 70000, 120000, 200000])
print("--- Salary distribution ---")
print(f"Mean: {np.mean(salaries):.0f}") # pulled up by outliers
print(f"Median: {np.median(salaries):.0f}") # more representative center
print(f"Skewness: {stats.skew(salaries):.2f}") # positive = right skew
# Left-skewed: most values are high, a few are very low
exam_scores = np.array([40, 68, 75, 80, 82, 85, 88, 90, 91, 92, 93, 95, 98])
print("\n--- Exam score distribution ---")
print(f"Mean: {np.mean(exam_scores):.1f}")
print(f"Median: {np.median(exam_scores):.1f}")
print(f"Skewness: {stats.skew(exam_scores):.2f}") # negative = left skewВывод:
--- Salary distribution ---
Mean: 59769
Median: 40000
Skewness: 2.16
--- Exam score distribution ---
Mean: 82.8
Median: 88.0
Skewness: -1.77При наличии скошенности среднее значение является вводящей в заблуждение мерой центра. Медиана обычно является лучшей сводной статистикой для скошенных данных.
Равномерное распределение
При равномерном распределении каждое значение (или диапазон значений) равновероятно. Честный шестигранный кубик даёт дискретное равномерное распределение; случайный выбор числа с плавающей точкой в диапазоне от 0 до 1 даёт непрерывное равномерное распределение.
import numpy as np
rng = np.random.default_rng(seed=42)
# Continuous uniform distribution between 0 and 1
uniform_data = rng.uniform(low=0, high=1, size=10000)
print(f"Mean: {np.mean(uniform_data):.3f} (expected 0.500)")
print(f"Std: {np.std(uniform_data):.3f} (expected ~0.289)")
print(f"Min: {np.min(uniform_data):.3f}")
print(f"Max: {np.max(uniform_data):.3f}")Вывод:
Mean: 0.497 (expected 0.500)
Std: 0.288 (expected ~0.289)
Min: 0.000
Max: 1.000Равномерные распределения встречаются при случайной выборке, аугментации данных и в качестве априорных распределений в байесовских моделях.
Сводная статистика распределения в Python
Перед построением модели всегда вычисляйте быструю сводку для каждого признака. NumPy и SciPy вместе охватывают ключевые статистические показатели:
import numpy as np
from scipy import stats
data = np.array([12, 15, 14, 10, 18, 14, 13, 16, 14, 12])
print("--- Distribution Summary ---")
print(f"Mean: {np.mean(data):.1f}")
print(f"Median: {np.median(data):.1f}")
print(f"Std: {np.std(data, ddof=1):.2f}") # sample std deviation
print(f"Min: {np.min(data)}")
print(f"Max: {np.max(data)}")
print(f"Skewness: {stats.skew(data):.3f}") # near 0 = symmetricВывод:
--- Distribution Summary ---
Mean: 13.8
Median: 14.0
Std: 2.25
Min: 10
Max: 18
Skewness: 0.200Значение асимметрии около 0.200 указывает на то, что данные приблизительно симметричны — нет выраженного наклона ни в одну из сторон.
Проверка нормальности
Некоторые алгоритмы явно предполагают, что признаки распределены нормально. Тест Шапиро-Уилка — наиболее надёжный тест для малых выборок (n < 5 000). Он возвращает тестовую статистику W и p-значение. P-значение больше 0,05 означает, что вы не можете отвергнуть гипотезу о нормальности данных.
import numpy as np
from scipy import stats
rng = np.random.default_rng(seed=42)
# Sample from a normal distribution
normal_sample = rng.normal(loc=0, scale=1, size=50)
stat, p = stats.shapiro(normal_sample)
print(f"Shapiro-Wilk: W={stat:.3f}, p={p:.3f}")
if p > 0.05:
print("Data appears to be normally distributed.")
else:
print("Data does not appear to be normally distributed.")Вывод:
Shapiro-Wilk: W=0.984, p=0.730
Data appears to be normally distributed.Когда применять: используйте тест Шапиро-Уилка, когда допущения модели явно требуют нормальности — например, перед применением параметрического t-критерия или линейного дискриминантного анализа. Многие современные алгоритмы (градиентный бустинг, случайные леса, нейронные сети) нечувствительны к форме распределения признаков, поэтому этот тест нужен не всегда.
Почему форма распределения важна для моделирования
| Ситуация | Что это означает | Что делать |
|---|---|---|
| Нормальные признаки | Стандартные допущения выполняются | Используйте параметрические модели как есть |
| Правосторонне скошенные признаки | Среднее завышено; выбросы доминируют | Примените логарифмическое или квадратно-корневое преобразование |
| Левосторонне скошенные признаки | Малые значения являются выбросами | Примените возведение в квадрат или отражение |
| Равномерные признаки | Нет центральной концентрации | Как правило, допустимо; нормализуйте для дистанционных моделей |
| Мультимодальные признаки | Несколько кластеров | Рассмотрите разбиение данных или шаг кластеризации |
Обработка скошенных данных с помощью логарифмического преобразования
Распространённым способом борьбы с правосторонней скошенностью является логарифмическое преобразование, которое сжимает большие значения и растягивает малые, часто приближая распределение к нормальному.
import numpy as np
from scipy import stats
rng = np.random.default_rng(seed=7)
# Simulate log-normally distributed incomes (a common real-world pattern)
incomes = rng.lognormal(mean=10.5, sigma=0.5, size=1000)
print(f"Before transform — skewness: {stats.skew(incomes):.2f}")
log_incomes = np.log(incomes)
print(f"After log transform — skewness: {stats.skew(log_incomes):.2f}")Вывод:
Before transform — skewness: 1.44
After log transform — skewness: 0.01После логарифмического преобразования асимметрия снижается с 1.44 до почти нуля, делая данные значительно более подходящими для моделей, предполагающих нормальность.
Примечание: логарифмическое преобразование требует, чтобы все значения были строго положительными. Добавьте небольшую константу (например, np.log(x + 1)), если ваши данные содержат нули.
Визуализация распределения данных
Визуализация — самый быстрый способ исследовать распределение. Гистограмма делит значения на интервалы и показывает, сколько наблюдений попадает в каждый интервал. Используйте гистограммы Matplotlib для их построения:
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng(seed=42)
data = rng.normal(loc=170, scale=10, size=500)
plt.figure(figsize=(8, 4))
plt.hist(data, bins=30, color='steelblue', edgecolor='white')
plt.title("Height Distribution (Normal)")
plt.xlabel("Height (cm)")
plt.ylabel("Frequency")
plt.tight_layout()
plt.show()Для более быстрого обзора нескольких признаков одновременно используйте диаграмму рассеяния, чтобы искать связи между распределениями.
Распределение и масштабирование признаков
Если признаки имеют очень разные распределения или масштабы, алгоритмы на основе расстояний (метод k ближайших соседей, SVM, кластеризация k-means) будут определяться признаками с наибольшим диапазоном. Масштабирование признаков — стандартное решение: StandardScaler нормализует каждый признак до среднего 0 и стандартного отклонения 1, а MinMaxScaler сжимает значения в диапазон [0, 1].
Выбирайте метод масштабирования на основе базового распределения:
- Нормальное распределение →
StandardScaler(удаляет среднее, масштабирует до единичной дисперсии) - Равномерное или ограниченное распределение →
MinMaxScaler(сохраняет форму) - Сильно скошенное с выбросами →
RobustScaler(использует медиану и межквартильный размах, игнорируя экстремальные значения)
Итоги
- Нормальное распределение: симметричная колоколообразная кривая; среднее ≈ медиана; подходит для параметрических моделей.
- Скошенное распределение: асимметричное; среднее смещено в сторону хвоста; логарифмическое или степенное преобразования могут уменьшить скошенность.
- Равномерное распределение: все значения равновероятны; встречается при случайной выборке и в качестве неинформативных априорных распределений.
- Используйте
scipy.stats.skew()для измерения асимметрии иscipy.stats.shapiro()для формальной проверки нормальности. - Всегда исследуйте распределения перед построением модели — форма влияет на выбор алгоритма, способ преобразования признаков и стратегию масштабирования.
Далее изучите нормальное распределение данных для более глубокого рассмотрения распределения Гаусса и работы с ним в SciPy.