Гистограмма с разной шириной интервалов: почему высота не равна вероятности
Сроки доставки разбили на интервалы 0–1 и 1–4 дня. В обоих по два заказа, но столбцы плотности имеют разную высоту. Это ожидаемо: широкий интервал распределяет ту же вероятность по большей ширине.
Разделить количество, долю и плотность
Гистограмма количества показывает число наблюдений в интервале. Если поделить числа на общий объём, получатся вероятностные массы: их сумма равна единице для покрытых наблюдений. Плотность дополнительно делит массу на ширину интервала. Теперь вероятность описывает площадь столбца, а не его высоту.
При одинаковой ширине разница часто незаметна: все высоты отличаются от долей одним коэффициентом. При неравных границах сумма плотностей обычно не равна единице. Нормировка density=True в np.histogram делает единичной сумму произведений высот на ширины.
Проверить высоту через площадь
В примере два значения находятся в первом интервале ширины один день, два — во втором ширины три дня. Массы одинаковы: по половине. Плотность первого равна 0.5 на день, второго — одной шестой на день. Умножение на ширины возвращает две половины.
Задаём bins явно и проверяем, что все наблюдения вошли в диапазон. Значения за его пределами np.histogram не учитывает; после этого density нормируется по оставшейся части. Иначе график способен скрыть длинные доставки за последней границей.
import numpy as np
values = np.array([0.2, 0.8, 2.0, 3.0])
bins = np.array([0., 1., 4.])
counts, edges = np.histogram(values, bins=bins)
density, _ = np.histogram(values, bins=bins, density=True)
width = np.diff(edges)
mass = counts / counts.sum()
print({"counts": counts.tolist(), "width": width.tolist(),
"density": density.tolist(), "mass_from_area": (density * width).tolist()})
assert counts.sum() == len(values)
assert np.allclose(mass, [0.5, 0.5])
assert np.allclose(density * width, mass)
assert np.isclose(np.sum(density * width), 1)
assert not np.isclose(density.sum(), 1)Количество заказов в обоих интервалах одинаково, но плотность второго ниже втрое. Это не означает, что вероятность второго интервала втрое меньше: площади равны. Подпись оси должна уточнять, показывается количество, доля или плотность на день.
Сравнить два периода на одной сетке
Для сравнения распределений используйте одинаковые границы. Если автоматически подобрать bins отдельно для каждого месяца, столбцы могут описывать разные диапазоны. Разница высот тогда смешает изменение данных с изменением сетки. Сохраняйте также размер каждой выборки.
У интервалов np.histogram левая граница включена, правая обычно исключена; последний интервал включает правую границу. Проверьте значения ровно на порогах, если бизнес сравнивает число доставок до одного дня. Правило может отличаться от ранее согласованного сегментирования.
Широкий последний интервал скрывает внутреннюю структуру длинного хвоста. По нему нельзя восстановить отдельную вероятность доставки больше конкретного порога внутри корзины. Для такого вопроса считайте условие по исходным значениям или задавайте подходящие границы заранее.
В отчёте покажите число наблюдений вне диапазона и отсутствие значений отдельно. Высокая доля потерянного хвоста меняет смысл нормированной картинки. Контроль площади вместе с контролем покрытия позволяет отличить корректную плотность от красивого графика неполных данных.
Закрепить на практике
В бесплатном уроке агрегатов NumPy вы закрепите суммы и вычисления по массивам. После этой базы разберите плотность из статьи: восстановите вероятностные массы как произведение высот на ширины. Откройте бесплатный урок и задачи. Полная последовательность тем — в бесплатном курсе.
Продолжить на практике
- NumPy и pandas: практический тренажёр ↗
Бесплатный тренажёр NumPy и pandas для тех, кто знает основы Python. Научитесь очищать таблицы, объединять данные и собирать отчёты: 31 урок и 124 задачи с кодом и автоматической проверкой.
