Box plots — диаграммы размаха в Matplotlib

Создано: 24.12.2025

Диаграммы размаха, или "ящики с усами", позволяют представить распределения вероятностей в компактном виде путем визуализации их основных характеристик: медианы, квартилей, минимума и максимума, выбросов (что они значат, описано ниже). Рассмотрим усиковую диаграмму в сравнении с гистограммой на примере нормального распределения. В модуле pyplot библиотеки Matplotlib коробчатые диаграммы строятся с помощью функции/метода boxplot.

import numpy as np
import matplotlib.pyplot as plt

a = np.random.normal(50, 3, 500)

fig, axs = plt.subplots(2, 1, figsize=(6, 8))

axs[0].hist(a, bins=20, rwidth=0.95)

axs[1].boxplot(a, vert=0, labels='a')  # параметры зависят от версии
axs[1].grid(axis='x')

plt.show()
Сравнение гистограммы и диаграммы размаха в Matplotlib

По умолчанию боксплоты выводятся вертикально, мы положили его горизонтально, чтобы было легче сравнивать с гистограммой. Представьте, что полученный с помощью NumPy массив a был неявно упорядочен по возрастанию. Оранжевая линия внутри ящика — это медиана. Ее не надо путать с арифметически средним, медиана — это срединное значение. Медиана определяется через упорядочение массива. Ей будет являться число, которое находится в центре такового. Если элементов в массиве четное количество, то два стоящих в середине складываются и делятся на два (по-сути находится среднее двух медиан).

Медиана одновременно является 0,5-м квартилем (или 50%-м процентилем). Квартили делят упорядоченный массив на четыре равные части. На диаграмме размаха сам ящик (коробка, блок) формируется между 0,25-м до 0,75-м квартилями и его размер обозначает диапазон значений, которые укладываются в эти "примедиальные" квартили (межквартильный размах). Если ящик маленький, это говорит о том, что в массиве есть много чисел с близкими к медиане значениями. Их количества достаточно, чтобы занять все места массива, отводимые для срединных квартилей.

Если выбросы отсутствуют, то черточки на концах усов обозначают минимальное и максимальное значения в массиве. В его упорядоченной версии это будут значения первого и последнего элемента.

Чтобы определить, если ли выбросы по обе стороны, из значения начала первого квартиля (0,25-й) вычитают полуторную величину межквартильного интервала, а к началу третьего квартиля (0,75-й) добавляют полуторную величину межквартильного размаха. Полученные значения определяют длину усов боксплота, их концы — края статистически значимой выборки. Если есть значения, выходящие за концы, значит есть выбросы.

     Q1-1.5IQR   Q1  медиана  Q3   Q3+1.5IQR
                  |-----:-----|
  o      |--------|     :     |--------|    o  o
                  |-----:-----|
выброс            <----------->           выбросы
                       IQR

Расстояния между различными частями ящика позволяют определить степень дисперсии и асимметрии данных.

С помощью параметров notch (наличие выемок) и bootstrap (рекомендуемые значения от 1000 до 10000) можно отображать в виде зарубок и указывать так называемый доверительный интервал (см. последний пример этого урока).

Помимо того, что умение интерпретировать диаграммы размаха позволяет одним взглядом оценивать сами распределения, "ящики с усами" удобны для сравнения их между собой (пример ниже взят из справки по Matplotlib).

np.random.seed(19680801)
fruit_weights = (
    np.random.normal(130, 10, size=100),
    np.random.normal(125, 20, size=100),
    np.random.normal(120, 30, size=100)
)
labels = ['peaches', 'oranges', 'tomatoes']
colors = ['peachpuff', 'orange', 'tomato']

fig, ax = plt.subplots()
ax.set_ylabel('fruit weight (g)')

bplot = ax.boxplot(fruit_weights, patch_artist=True, labels=labels)

for patch, color in zip(bplot['boxes'], colors):
    patch.set_facecolor(color)
Сравнение нормальных распределений с разным стандартным отклонением через ящики с усами

Наиболее выраженные отличия можно увидеть, сравнивая боксплоты разных распределений:

data = (
    np.random.uniform(80, 120, size=100),
    np.random.normal(100, 10, size=100),
    np.random.pareto(size=100, a=10) * 200,
    np.random.binomial(size=100, n=10, p=0.8) * 10,
)

plt.boxplot(data, patch_artist=True,
            labels=('uniform', 'normal', 'pareto', 'binomial'))
plt.ylabel('Values')
Сравнение распределений равномерного, нормального, Парето, биномиального с помощью усиковых диаграмм

Кроме медианы, boxplot() позволяет обозначать на диаграмме среднее значение. Для этого используется параметр showmeans.

plt.boxplot(data, showmeans=True, meanline=True, notch=True,
            labels=('uniform', 'normal', 'pareto', 'binomial'))
Медиана, среднее значение и доверительный интервал на диаграмме размаха в Matplotlib

Получить сами данные можно с помощью модуля cbook:

import matplotlib.cbook as cbook

a = np.random.normal(50, 3, 500)
plt.boxplot(a)

stats = cbook.boxplot_stats(a)
for i in stats[0]:
    print(i, stats[0][i])
mean 49.91551182353907
iqr 3.9511495739328666
cilo 49.534831196709234
cihi 50.08967164820666
whishi 57.55879344804599
whislo 42.7640992450584
fliers [41.83158929 38.73954884 59.32456169 58.43105926 58.2007655 ]
q1 47.8876446243917
med 49.81225142245795
q3 51.838794198324564

Также в NumPy есть свои функции для квартилей, процентилей, медианы и др.