W3docs

Гистограммы Matplotlib в Python — Полное руководство

Узнайте, как создавать и настраивать гистограммы в Python с Matplotlib: бины, плотность, KDE, кумулятивные и составные гистограммы.

Функция hist() в Matplotlib позволяет легко визуализировать распределение набора данных по числовому диапазону. Эта глава охватывает всё — от минимальной первой гистограммы до практических приёмов, которые вы будете использовать в реальных проектах: выбор правильного количества бинов, отображение плотности вместо сырых подсчётов, наложение кривой KDE, сравнение нескольких распределений и сохранение результата в файл.

Перед началом убедитесь, что Matplotlib и NumPy установлены:

pip install matplotlib numpy

Если вы новичок в библиотеке, сначала ознакомьтесь с главами Введение в Matplotlib и Начало работы.

Что такое гистограмма?

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

Используйте гистограмму, когда хотите ответить на такие вопросы:

  • Где сосредоточена большая часть данных?
  • Является ли распределение примерно нормальным или оно смещено?
  • Есть ли пробелы или несколько пиков (бимодальные данные)?
  • Есть ли выбросы, далеко отстоящие от основной массы данных?

Создание базовой гистограммы

Передайте одномерный array или список в plt.hist(), и Matplotlib автоматически выберет количество бинов:

import matplotlib.pyplot as plt
import numpy as np

# Reproducible example: 1 000 values from a normal distribution
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

plt.hist(data)

plt.title('Basic Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()

plt.tight_layout() предотвращает обрезку подписей осей — полезная привычка добавлять его перед каждым вызовом show() или savefig().

Выбор количества бинов

Параметр bins является наиболее важным регулятором гистограммы. Слишком мало бинов скрывает структуру; слишком много бинов создаёт шум.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

fig, axes = plt.subplots(1, 3, figsize=(12, 4))

for ax, n_bins in zip(axes, [5, 30, 100]):
    ax.hist(data, bins=n_bins, color='steelblue', edgecolor='white')
    ax.set_title(f'bins={n_bins}')
    ax.set_xlabel('Value')
    ax.set_ylabel('Frequency')

plt.suptitle('Effect of Bin Count', y=1.02)
plt.tight_layout()
plt.show()

Практические рекомендации:

  • 5–10 бинов — полезно для очень малых наборов данных (n < 50) или быстрого обзора.
  • 20–50 бинов — хороший вариант по умолчанию для большинства наборов данных (n = 100–10 000).
  • 50–100+ бинов — подходит для больших наборов данных (n > 10 000), где важна детальная структура.
  • Вместо числа можно передать строковое правило: bins='auto', bins='fd' (Фридман–Диаконис) или bins='sturges' — Matplotlib делегирует вычисления функции np.histogram_bin_edges() из NumPy.

Вы также можете передать явный список граней бинов для неравномерного разбиения:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.exponential(scale=2, size=1000)

# Finer bins near 0, coarser bins in the tail
edges = [0, 0.5, 1, 1.5, 2, 3, 4, 6, 8, 12]

plt.hist(data, bins=edges, color='darkorange', edgecolor='white')
plt.title('Custom Bin Edges (Exponential Data)')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()

Настройка внешнего вида

Цвет, прозрачность и граница

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=5, scale=1.5, size=800)

plt.hist(
    data,
    bins=30,
    color='steelblue',
    edgecolor='white',   # thin white line between bars
    linewidth=0.5,
    alpha=0.85,          # slight transparency
)

plt.title('Styled Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()

alpha (0 = полностью прозрачный, 1 = полностью непрозрачный) особенно полезен при наложении нескольких гистограмм, чтобы столбцы не скрывали друг друга.

Удаление лишних элементов

Удаление верхней и правой рамок придаёт гистограмме более чистый вид:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

fig, ax = plt.subplots(figsize=(8, 4))
ax.hist(data, bins=30, color='cornflowerblue', edgecolor='white')

ax.set_title('Clean Histogram')
ax.set_xlabel('Value')
ax.set_ylabel('Frequency')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

Гистограммы плотности

По умолчанию hist() отображает сырые подсчёты на оси Y. Передайте density=True, чтобы нормализовать ось Y так, чтобы суммарная площадь всех столбцов равнялась 1. Это превращает гистограмму в оценку вероятностной плотности, что упрощает сравнение наборов данных разного размера.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

plt.hist(data, bins=30, density=True, color='mediumseagreen', edgecolor='white')

plt.title('Density Histogram')
plt.xlabel('Value')
plt.ylabel('Probability Density')
plt.tight_layout()
plt.show()

Примечание: значения на оси Y — это плотности, а не вероятности. Умножьте плотность на ширину бина, чтобы получить вероятность для этого бина.

Наложение кривой KDE

Оценка плотности ядра (KDE) — это гладкая кривая, приближающая лежащее в основе вероятностное распределение. Наложение её на гистограмму плотности даёт наглядное представление о форме распределения. Используйте scipy.stats.gaussian_kde для её вычисления:

import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

fig, ax = plt.subplots(figsize=(8, 4))

# Density histogram
ax.hist(data, bins=30, density=True,
        color='steelblue', edgecolor='white', alpha=0.6, label='Histogram')

# KDE curve
xs = np.linspace(data.min(), data.max(), 300)
kde = gaussian_kde(data)
ax.plot(xs, kde(xs), color='navy', linewidth=2, label='KDE')

ax.set_title('Histogram with KDE Overlay')
ax.set_xlabel('Value')
ax.set_ylabel('Probability Density')
ax.legend()
plt.tight_layout()
plt.show()

Установите SciPy, если он ещё не доступен:

pip install scipy

Сравнение нескольких распределений

Чтобы сравнить два и более распределения на одних осях, вызовите hist() несколько раз и используйте alpha для полупрозрачности столбцов. Установите одинаковые bins для обоих, чтобы ширина столбцов была сопоставимой:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
group_a = rng.normal(loc=0,   scale=1,   size=500)
group_b = rng.normal(loc=2,   scale=1.5, size=500)

shared_bins = np.linspace(-5, 8, 40)

plt.hist(group_a, bins=shared_bins, alpha=0.6, color='steelblue',   label='Group A')
plt.hist(group_b, bins=shared_bins, alpha=0.6, color='darkorange',  label='Group B')

plt.title('Overlapping Histograms')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.legend()
plt.tight_layout()
plt.show()

Определение shared_bins через np.linspace() гарантирует, что обе гистограммы используют одинаковые грани бинов, и их столбцы выровнены визуально.

Параллельные (составные) гистограммы

Когда наложение затрудняет чтение отдельных распределений, используйте plt.subplots() для размещения их рядом:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
group_a = rng.normal(loc=0,   scale=1,   size=500)
group_b = rng.normal(loc=2,   scale=1.5, size=500)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), sharey=True)

ax1.hist(group_a, bins=30, color='steelblue',  edgecolor='white')
ax1.set_title('Group A')
ax1.set_xlabel('Value')
ax1.set_ylabel('Frequency')

ax2.hist(group_b, bins=30, color='darkorange', edgecolor='white')
ax2.set_title('Group B')
ax2.set_xlabel('Value')

plt.suptitle('Side-by-Side Histograms')
plt.tight_layout()
plt.show()

sharey=True синхронизирует обе подграфики по одной шкале оси Y, чтобы высоты столбцов можно было непосредственно сравнивать. Дополнительные варианты компоновки описаны в главе Подграфики Matplotlib.

Кумулятивные гистограммы

Передайте cumulative=True, чтобы построить гистограмму, в которой каждый столбец представляет общее количество наблюдений до и включая данный бин. Это полезно для ответа на вопросы вроде «какая доля значений меньше X?»:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

plt.hist(data, bins=30, cumulative=True, density=True,
         color='mediumpurple', edgecolor='white')

plt.title('Cumulative Density Histogram')
plt.xlabel('Value')
plt.ylabel('Cumulative Probability')
plt.tight_layout()
plt.show()

В сочетании с density=True кумулятивная гистограмма становится ступенчатым приближением эмпирической функции распределения (CDF). Ось Y принимает значения от 0 до 1.

Ориентация гистограммы

Передайте orientation='horizontal', чтобы рисовать столбцы, вытянутые влево от оси Y. Это редко используется по умолчанию, но соответствует компоновке маргинальных распределений на диаграммах рассеяния:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=500)

plt.hist(data, bins=20, orientation='horizontal',
         color='tomato', edgecolor='white')

plt.title('Horizontal Histogram')
plt.ylabel('Value')
plt.xlabel('Frequency')
plt.tight_layout()
plt.show()

Сохранение гистограммы в файл

Используйте plt.savefig() перед plt.show() (или вместо него). Формат задаётся расширением файла:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

plt.hist(data, bins=30, color='steelblue', edgecolor='white')
plt.title('Distribution of Sample Data')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()

plt.savefig('histogram.png', dpi=150)   # raster, good for web
plt.savefig('histogram.pdf')            # vector, good for print/publication
plt.show()

Распространённые форматы: png, pdf, svg, eps. Используйте svg или pdf, когда нужно масштабируемое изображение типографского качества.

Основные параметры hist() — краткий справочник

ПараметрТипОписание
binsint, список или stringКоличество бинов, явные грани или имя правила ('auto', 'fd', 'sturges')
densitybooleanНормализация так, чтобы суммарная площадь = 1 (вероятностная плотность)
cumulativebooleanКаждый столбец показывает кумулятивный подсчёт/плотность до этого бина
orientationstring'vertical' (по умолчанию) или 'horizontal'
colorstringЦвет заливки столбца
edgecolorstringЦвет границы столбца; 'white' создаёт чёткий разделитель
alphafloat 0–1Прозрачность; устанавливайте ниже 1 при наложении гистограмм
labelstringПодпись легенды для данной гистограммы
histtypestring'bar' (по умолчанию), 'step', 'stepfilled'
range(min, max)Обрезать данные до этого диапазона перед разбиением на бины

Распространённые ошибки

  • Случайные зерна. np.random.randn() выдаёт разные значения при каждом запуске. Используйте np.random.default_rng(seed) для воспроизводимых примеров.
  • density против normed. Старый параметр normed=True был удалён в Matplotlib 3.x. Всегда используйте density=True.
  • Сравнение гистограмм с разными объёмами выборок. Сырые частотные столбцы несопоставимы при разных размерах групп — используйте density=True для нормализации обеих.
  • Дискретные целочисленные данные. Для целых чисел (например, броски кубика, оценки в опросах) устанавливайте грани бинов на полуцелые значения — bins=[0.5, 1.5, 2.5, ..., 6.5] — чтобы каждое целое число попадало ровно в один бин без неоднозначности на границах.
  • plt.show() очищает фигуру. Если вы вызываете show(), а затем savefig(), файл окажется пустым. Всегда вызывайте savefig() до show().

Связанные главы

Was this page helpful?