Нормировка плотности и ядерная оценка (KDE)

Чтобы сравнивать распределения разного размера, гистограмму превращают в плотность.

Нормировка плотности — деление высот гистограммы так, чтобы суммарная площадь столбцов равнялась 1; тогда высота — это оценка плотности вероятности, а не число точек.

Зачем нормировать

Если у вас две выборки — 100 и 10000 наблюдений — их «сырые» гистограммы (по числу точек) несравнимы: вторая просто в сто раз выше. Чтобы сравнить форму распределений, переходят к плотности: высота бина равна $\dfrac{n_i}{n \cdot h}$, где $n_i$ — число точек в бине, $n$ — общее число, $h$ — ширина бина. Тогда площадь под гистограммой равна 1 независимо от размера выборки, и две формы можно наложить друг на друга.

Считаем плотность

Проверим, что после нормировки площадь равна 1.

data = [4,5,5,6,6,6,7,7,7,7,8,8,8,9,9,10,11,12]
k = 4
lo, hi = min(data), max(data)
h = (hi - lo) / k
n = len(data)
counts = [0]*k
for v in data:
    counts[min(int((v-lo)/h), k-1)] += 1

density = [c / (n * h) for c in counts]
area = sum(d * h for d in density)   # площадь = сумма (плотность * ширина)

for i, dns in enumerate(density):
    print(f"бин {i}: плотность = {dns:.3f}")
print("суммарная площадь =", round(area, 3))

Вывод:

бин 0: плотность = 0.083
бин 1: плотность = 0.194
бин 2: плотность = 0.139
бин 3: плотность = 0.083
суммарная площадь = 1.0

Площадь ровно 1 — теперь это оценка плотности вероятности, сравнимая между выборками любого размера.

KDE: гладкая альтернатива

Гистограмма зависит от положения границ бинов — сдвиньте их, и форма поменяется. Ядерная оценка плотности (KDE) снимает эту проблему: вместо столбцов на каждую точку кладётся маленький гладкий «холмик» (ядро, обычно гауссиан), и они суммируются в плавную кривую. Формула: $\hat{f}(x) = \dfrac{1}{n h}\sum_{i=1}^{n} K\!\left(\dfrac{x - x_i}{h}\right)$, где $K$ — ядро, $h$ — ширина полосы (bandwidth), аналог ширины бина.

import math

data = [4,5,6,6,7,7,7,8,8,9,10,12]
h = 1.0   # ширина полосы (bandwidth)

def gauss(u):
    return math.exp(-0.5*u*u) / math.sqrt(2*math.pi)

def kde(x):
    n = len(data)
    return sum(gauss((x - xi)/h) for xi in data) / (n*h)

for x in range(4, 13):
    val = kde(x)
    print(f"x={x:2d}  плотность={val:.3f}  {'*'*int(val*40)}")

Вывод:

x= 4  плотность=0.064  **
x= 5  плотность=0.108  ****
x= 6  плотность=0.161  ******
x= 7  плотность=0.190  *******
x= 8  плотность=0.161  ******
x= 9  плотность=0.108  ****
x=10  плотность=0.068  **
x=11  плотность=0.046  *
x=12  плотность=0.038  *

Получилась гладкая оценка плотности без зависимости от границ бинов. Пик у $x=7$ соответствует моде данных.

Как работает под капотом

KDE — это свёртка эмпирического распределения (набора дельта-функций в точках данных) с ядром. Ширина полосы $h$ управляет гладкостью так же, как ширина бина в гистограмме: малое $h$ даёт изрезанную кривую (переобучение под шум), большое — переглаживает и сливает моды. Выбор $h$ — тот же компромисс смещение–дисперсия. Правило Сильвермана даёт разумную автоматическую оценку $h$.

Граничный эффект и выбор ядра

У KDE есть коварная ловушка на ограниченных величинах. Если данные неотрицательны (время, концентрация, длина) и есть точки около нуля, гауссово ядро «протечёт» в область отрицательных значений, нарисовав плотность там, где данные физически невозможны. На графике это выглядит как мягкий «хвост» влево от нуля — артефакт метода, а не свойство данных. Решения: отражать данные относительно границы, использовать ядра с ограниченным носителем или просто честно обрезать кривую на границе и предупредить читателя.

Выбор формы ядра (гаусс, Епанечников, прямоугольное) на практике влияет слабо — куда важнее ширина полосы. Гауссово ядро популярно за гладкость, ядро Епанечникова оптимально в смысле минимума ошибки, но разница между ними обычно незаметна глазом. А вот ошибка в bandwidth видна сразу: слишком малый превращает плотность в частокол пиков по числу точек, слишком большой сливает реальные моды в один холм. Поэтому, как и с бинами, проверяйте несколько значений ширины.

Частые ошибки

  • Сравнивать сырые гистограммы выборок разного размера — нужна нормировка плотности.
  • KDE с дефолтным bandwidth — может выдумать или стереть моды; проверяйте 2–3 значения.
  • KDE на ограниченной величине (например, неотрицательной) — ядро «протекает» за границу в физически невозможную область.
  • Подмена данных гладкой кривой — KDE скрывает дискретность и реальное число точек; на малых выборках показывайте и сами точки (rug plot).

Итог

  • Нормировка делает площадь равной 1 — формы сравнимы между выборками.
  • KDE — гладкая оценка плотности, не зависящая от границ бинов.
  • Bandwidth играет роль ширины бина: тот же компромисс гладкости.
  • На малых выборках дополняйте KDE точками данных.
Проверьте себя
1. Зачем нормировать гистограмму в плотность?
AЧтобы сделать её цветной
BЧтобы площадь стала равна 1 и формы выборок разного размера были сравнимы
CЧтобы убрать выбросы
DЧтобы ускорить расчёт
2. В чём преимущество KDE перед гистограммой?
AKDE всегда точнее
BKDE даёт гладкую оценку, не зависящую от положения границ бинов
CKDE не требует данных
DKDE быстрее считается
3. Что управляет гладкостью KDE?
AЧисло точек
BШирина полосы (bandwidth) h — аналог ширины бина
CЦвет кривой
DРазмер фигуры