Привет! Ты уже умеешь строить столбчатые диаграммы для сравнения категорий. Но что, если нужно понять, как распределены числовые данные? Например, сколько людей в каждом возрастном диапазоне или как часто встречаются зарплаты определённого размера? Для этого есть гистограммы.
В этой статье мы разберём:
- Что такое гистограмма и чем она отличается от столбчатой диаграммы
- Как строить гистограммы с
hist() - Настройка количества интервалов (
bins) - Нормализация гистограммы (
density) - Сравнение нескольких распределений
- Добавление кривой нормального распределения
- Что нужно знать перед началом
- Основная часть
- Что такое гистограмма?
- Простая гистограмма
- Настройка гистограммы
- Количество бинов (bins)
- Цвет и прозрачность
- Вертикальная (горизонтальная) ориентация
- Нормализация гистограммы (density)
- Сравнение нескольких распределений
- Добавление кривой нормального распределения
- Сравнение групп с помощью гистограмм
- Кумулятивная гистограмма
- Практический пример: анализ возраста клиентов
- Гистограмма с логарифмической шкалой
- Гистограмма с подписями на столбцах
- Задачи для закрепления
- Нюансы и подводные камни
- Выбор количества бинов
- density=True vs нормировка
- Выбросы
- Частые ошибки и как их избежать
- Ошибка 1: Путаница между hist() и bar()
- Ошибка 2: Слишком много бинов
- Ошибка 3: Забыл параметр bins для лучшей читаемости
- Шпаргалка
- Заключение
- КВИЗ
- Что дальше?
Что нужно знать перед началом
Для этого урока тебе понадобится:
- Установленный Matplotlib (
pip install matplotlib) - Базовое понимание графиков из предыдущих статей
- Желание анализировать распределения данных
Совет: Гистограмма — это лучший способ «увидеть» распределение данных. Она показывает, где данных много, а где мало, есть ли выбросы и какова форма распределения.
Основная часть
Что такое гистограмма?
Гистограмма — это график, который показывает распределение числовых данных. Данные разбиваются на интервалы (бины), и для каждого интервала показывается, сколько значений в него попало.
Отличие от столбчатой диаграммы:
| Гистограмма | Столбчатая диаграмма |
|---|---|
| Для числовых данных | Для категориальных данных |
| Интервалы имеют порядок | Категории могут быть в любом порядке |
| Столбцы касаются друг друга | Столбцы разделены промежутками |
Совет: Если столбцы не касаются друг друга — это столбчатая диаграмма. Если касаются — гистограмма.
Простая гистограмма
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
# Генерируем нормально распределённые данные
data = np.random.normal(0, 1, 1000)
# Строим гистограмму
plt.hist(data, bins=30)
plt.title('Простая гистограмма')
plt.xlabel('Значение')
plt.ylabel('Частота')
plt.grid(True, alpha=0.3)
plt.show()Совет:
bins=30— количество интервалов. Чем больше бинов, тем детальнее гистограмма. Для начала используй 20–30 бинов.
Настройка гистограммы
Количество бинов (bins)
plt.hist(data, bins=10, edgecolor='black', alpha=0.7, color='skyblue')
plt.title('10 бинов')
plt.show()plt.hist(data, bins=50, edgecolor='black', alpha=0.7, color='skyblue')
plt.title('50 бинов')
plt.show()Совет: Слишком мало бинов — теряется детализация. Слишком много — шум и пустые интервалы. Начни с
bins=30и корректируй.
Цвет и прозрачность
plt.hist(data, bins=30, color='green', alpha=0.7, edgecolor='black', linewidth=1)
plt.title('Гистограмма с настройками')
plt.show()Вертикальная (горизонтальная) ориентация
# Горизонтальная гистограмма
plt.hist(data, bins=30, orientation='horizontal', color='purple', edgecolor='black')
plt.title('Горизонтальная гистограмма')
plt.show()Нормализация гистограммы (density)
density=True показывает не абсолютные частоты, а плотность вероятности. Площадь под гистограммой становится равна 1. Это позволяет сравнивать данные разного размера и накладывать теоретические распределения.
# Нормализованная гистограмма
plt.hist(data, bins=30, density=True, alpha=0.7, edgecolor='black')
plt.title('Нормализованная гистограмма (плотность)')
plt.ylabel('Плотность')
plt.show()Совет:
density=Trueполезен для сравнения распределений разных выборок и для проверки, насколько данные соответствуют нормальному распределению.
Сравнение нескольких распределений
# Генерируем два распределения
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 0.8, 1000)
plt.hist(data1, bins=30, alpha=0.6, label='Распределение 1', density=True, edgecolor='black')
plt.hist(data2, bins=30, alpha=0.6, label='Распределение 2', density=True, edgecolor='black')
plt.title('Сравнение двух распределений')
plt.xlabel('Значение')
plt.ylabel('Плотность')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()Совет: Используй
density=Trueи полупрозрачность (alpha) для наложения гистограмм.
Добавление кривой нормального распределения
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
np.random.seed(42)
# Данные
data = np.random.normal(0, 1, 1000)
# Гистограмма
plt.hist(data, bins=30, density=True, alpha=0.7, edgecolor='black', label='Данные')
# Кривая нормального распределения
x = np.linspace(-4, 4, 100)
plt.plot(x, norm.pdf(x, 0, 1), color='red', linewidth=2, label='Нормальное распределение')
plt.title('Гистограмма с кривой нормального распределения')
plt.xlabel('Значение')
plt.ylabel('Плотность')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()Совет: Если данные хорошо ложатся на кривую нормального распределения — значит, они примерно нормальны. Это полезно для многих статистических методов.
Сравнение групп с помощью гистограмм
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
# Данные о зарплатах в двух отделах
sales = np.random.normal(50000, 10000, 200)
it = np.random.normal(70000, 15000, 200)
hr = np.random.normal(45000, 8000, 200)
# Группируем данные для boxplot (для сравнения)
data = [sales, it, hr]
labels = ['Продажи', 'IT', 'HR']
# Строим гистограммы для каждой группы
fig, axes = plt.subplots(1, 3, figsize=(12, 4), sharex=True, sharey=True)
for i, (group, label) in enumerate(zip(data, labels)):
axes[i].hist(group, bins=20, alpha=0.7, edgecolor='black', density=True)
axes[i].set_title(label)
axes[i].set_xlabel('Зарплата')
axes[i].grid(True, alpha=0.3)
# Добавляем среднее значение
mean = np.mean(group)
axes[i].axvline(mean, color='red', linestyle='--', linewidth=2, label=f'Среднее: {mean:.0f}')
axes[i].legend()
axes[0].set_ylabel('Плотность')
plt.suptitle('Сравнение распределений зарплат по отделам')
plt.tight_layout()
plt.show()Кумулятивная гистограмма
# Кумулятивная гистограмма (накопленная частота)
plt.hist(data, bins=30, cumulative=True, alpha=0.7, edgecolor='black')
plt.title('Кумулятивная гистограмма')
plt.xlabel('Значение')
plt.ylabel('Накопленная частота')
plt.grid(True, alpha=0.3)
plt.show()Совет: Кумулятивная гистограмма показывает, сколько процентов данных находится ниже определённого значения.
Практический пример: анализ возраста клиентов
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
# Данные о возрасте клиентов
ages = np.random.normal(35, 12, 500)
ages = np.clip(ages, 18, 70) # Ограничиваем от 18 до 70
# Анализ распределения
plt.figure(figsize=(10, 6))
# Гистограмма с настройками
plt.hist(ages, bins=20, alpha=0.7, edgecolor='black', density=True, color='steelblue')
# Добавляем среднее и медиану
mean_age = np.mean(ages)
median_age = np.median(ages)
plt.axvline(mean_age, color='red', linestyle='--', linewidth=2, label=f'Среднее: {mean_age:.1f}')
plt.axvline(median_age, color='green', linestyle='--', linewidth=2, label=f'Медиана: {median_age:.1f}')
# Кривая нормального распределения
x = np.linspace(18, 70, 100)
from scipy.stats import norm
plt.plot(x, norm.pdf(x, mean_age, np.std(ages)), color='darkred', linewidth=2, label='Нормальное распределение')
plt.title('Распределение возраста клиентов', fontsize=14, fontweight='bold')
plt.xlabel('Возраст', fontsize=12)
plt.ylabel('Плотность', fontsize=12)
plt.legend(fontsize=11)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Вывод статистик
print(f"Количество клиентов: {len(ages)}")
print(f"Средний возраст: {mean_age:.1f}")
print(f"Медианный возраст: {median_age:.1f}")
print(f"Стандартное отклонение: {np.std(ages):.1f}")
print(f"Минимальный возраст: {np.min(ages)}")
print(f"Максимальный возраст: {np.max(ages)}")Результат:
Количество клиентов: 500
Средний возраст: 34.9
Медианный возраст: 35.0
Стандартное отклонение: 11.9
Минимальный возраст: 18
Максимальный возраст: 70Гистограмма с логарифмической шкалой
# Логарифмическая шкала для y (полезно при больших разбросах)
plt.hist(data, bins=30, log=True, alpha=0.7, edgecolor='black')
plt.title('Гистограмма с логарифмической шкалой')
plt.ylabel('Логарифм частоты')
plt.grid(True, alpha=0.3)
plt.show()Гистограмма с подписями на столбцах
# Получаем значения гистограммы
counts, bins, patches = plt.hist(data, bins=20, alpha=0.7, edgecolor='black')
# Добавляем подписи
for count, bin_edge in zip(counts, bins[:-1]):
if count > 0:
plt.text(bin_edge + (bins[1] - bins[0])/2, count + 0.5, str(int(count)),
ha='center', va='bottom', fontsize=8)
plt.title('Гистограмма с подписями значений')
plt.show()Задачи для закрепления
Задача 1. Построй гистограмму для случайных 1000 точек из нормального распределения с параметрами mean=0, std=1.
Задача 2. Настрой гистограмму: bins=20, цвет blue, прозрачность 0.6, обводка black.
Задача 3. Добавь density=True и сравни гистограмму с теоретической кривой нормального распределения.
Задача 4. Построй кумулятивную гистограмму для тех же данных.
Задача 5. Сравни два распределения на одном графике с прозрачностью.
Ответы:
Задача 1.
import matplotlib.pyplot as plt
import numpy as np
data = np.random.normal(0, 1, 1000)
plt.hist(data, bins=30)
plt.show()Задача 2.
plt.hist(data, bins=20, color='blue', alpha=0.6, edgecolor='black')Задача 3.
from scipy.stats import norm
plt.hist(data, bins=30, density=True, alpha=0.7, edgecolor='black')
x = np.linspace(-4, 4, 100)
plt.plot(x, norm.pdf(x, 0, 1), color='red', linewidth=2)
plt.show()Задача 4.
plt.hist(data, bins=30, cumulative=True, alpha=0.7, edgecolor='black')
plt.show()Задача 5.
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 0.8, 1000)
plt.hist(data1, bins=30, alpha=0.5, label='Data1', density=True)
plt.hist(data2, bins=30, alpha=0.5, label='Data2', density=True)
plt.legend()
plt.show()Нюансы и подводные камни
Выбор количества бинов
- Слишком мало бинов → теряем детали
- Слишком много бинов → шум
- Используй правило:
bins = int(np.sqrt(len(data)))как стартовую точку
density=True vs нормировка
density=True делает площадь под гистограммой равной 1. Это полезно для сравнения с теоретическими распределениями.
Выбросы
Выбросы могут растянуть гистограмму и сделать её нечитаемой. Рассмотри обрезку данных.
Частые ошибки и как их избежать
Ошибка 1: Путаница между hist() и bar()
# bar() для числовых данных (неправильно)
plt.bar(data, data)
# hist() для числовых данных
plt.hist(data)Ошибка 2: Слишком много бинов
# Шумная гистограмма
plt.hist(data, bins=200)
# Оптимальное количество
plt.hist(data, bins=30)Ошибка 3: Забыл параметр bins для лучшей читаемости
# По умолчанию 10 бинов
plt.hist(data)
# Настроенные бины
plt.hist(data, bins=30)Шпаргалка
| Что нужно | Как пишется |
|---|---|
| Простая гистограмма | plt.hist(data) |
| Количество бинов | bins=30 |
| Цвет и прозрачность | color='blue', alpha=0.7 |
| Обводка | edgecolor='black' |
| Нормализация | density=True |
| Горизонтальная | orientation='horizontal' |
| Кумулятивная | cumulative=True |
| Логарифмическая шкала | log=True |
| Несколько гистограмм | несколько вызовов plt.hist() с alpha |
| Сравнение с нормальным распределением | plt.hist(... density=True), plt.plot(x, norm.pdf(x, mean, std)) |
Заключение
Сегодня мы научились:
- Строить гистограммы для визуализации распределений
- Настраивать количество бинов, цвет и прозрачность
- Сравнивать несколько распределений на одном графике
- Добавлять кривую нормального распределения
- Анализировать реальные данные с помощью гистограмм





