W3docs

Распределение данных в машинном обучении

Узнайте, как работают распределения данных в 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.

Was this page helpful?