Как построить гистограмму в pandas

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем нужна гистограмма

Гистограмма — первое, что делает аналитик при взгляде на новую числовую колонку. «Сколько стоят наши заказы?» → сумма бесполезна, среднее обманчиво, нужна гистограмма. Сразу видно: есть ли «горб», тяжёлый хвост, бимодальное распределение, выбросы.

Ошибиться здесь легко. Мало bins — потеряете структуру данных. Слишком много — увидите шум, а не сигнал. Не учтёте outliers — увидите один столбик на всю ширину, потому что один кит купил на 10 млн. Не используете alpha при сравнении групп — один график закрывает другой.

В статье — практическое руководство:

  • hist() и plot.hist() — pandas-способы
  • Количество bins: правило Sturges, Scott, sqrt(N)
  • Сравнение A/B-групп с alpha=0.5
  • Density vs frequency (когда что)
  • Log-scale и log-трансформация для тяжёлых хвостов
  • KDE через seaborn для сглаженного распределения
  • Boxplot vs histogram — когда что предпочесть

Базовый синтаксис

import pandas as pd

df['age'].hist()

Или через plot:

df['age'].plot.hist()

Оба дают один результат.

1. Настройка bins

df['age'].hist(bins=20)              # 20 корзин
df['age'].hist(bins=[0, 10, 20, 30]) # кастомные границы
df['age'].hist(bins='auto')          # авто-выбор

Правильное количество bins важно:

  • Мало — теряете детали
  • Много — шум / пустые bins

Правило большого пальца: bins = sqrt(N).

2. Несколько колонок на одном графике

import matplotlib.pyplot as plt

df[['age', 'score']].hist(figsize=(10, 4), bins=20)
plt.show()

Получатся два subplot.

3. Сравнение групп

Гистограммы контроль vs тест в A/B:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(10, 5))
df[df['group'] == 'control']['amount'].hist(
    ax=ax, bins=30, alpha=0.5, label='Control'
)
df[df['group'] == 'test']['amount'].hist(
    ax=ax, bins=30, alpha=0.5, label='Test'
)
ax.legend()
plt.show()

alpha=0.5 — полупрозрачность, чтобы видеть пересечения.

4. Density vs frequency

# frequency (количество)
df['amount'].hist()

# density (плотность, сумма = 1)
df['amount'].hist(density=True)

Density удобен для сравнения групп разного размера.

5. Логарифмическая шкала

Для данных с тяжёлым хвостом (доход, чеки):

import matplotlib.pyplot as plt

df['amount'].hist(bins=50)
plt.yscale('log')  # log по y
plt.xscale('log')  # log по x тоже иногда полезно

Или перед гистограммой применить log-трансформацию:

import numpy as np
np.log1p(df['amount']).hist(bins=50)

6. seaborn — красивее

import seaborn as sns

# простая гистограмма
sns.histplot(df['amount'], bins=30)

# с KDE-линией
sns.histplot(df['amount'], bins=30, kde=True)

# сравнение групп
sns.histplot(data=df, x='amount', hue='group', bins=30, alpha=0.5)

seaborn автоматически улучшает визуализацию.

7. Настройки matplotlib

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(12, 6))
df['amount'].hist(
    ax=ax,
    bins=30,
    color='steelblue',
    edgecolor='black',
    alpha=0.7
)
ax.set_title('Распределение сумм заказов')
ax.set_xlabel('Сумма, ₽')
ax.set_ylabel('Количество')
plt.grid(axis='y', alpha=0.3)
plt.show()

8. Boxplot — альтернатива

Иногда boxplot лучше гистограммы для сравнения групп:

df.boxplot(column='amount', by='group')
# или seaborn
sns.boxplot(data=df, x='group', y='amount')

9. Сохранение в файл

import matplotlib.pyplot as plt

df['amount'].hist(bins=30)
plt.savefig('histogram.png', dpi=300, bbox_inches='tight')
plt.close()

10. Работа с выбросами

Для метрик с тяжёлым хвостом обрежьте перед визуализацией:

# до P99
p99 = df['amount'].quantile(0.99)
df[df['amount'] < p99]['amount'].hist(bins=50)

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

1. Мало bins → «блоки»

df['age'].hist(bins=5)  # всё в 5 корзинах, детали теряются

2. Слишком много bins → шум

df['age'].hist(bins=500)  # 500 маленьких столбиков

3. Не указать alpha при сравнении групп

Без alpha один график закрывает другой.

4. Гистограмма на категориальных данных

Используйте barplot для категорий:

df['category'].value_counts().plot(kind='bar')
# или
sns.countplot(data=df, x='category')

5. Log без log1p при наличии нулей

log(0) = -inf. Используйте np.log1p(x) = log(1+x).

Связанные темы

FAQ

Сколько bins выбрать?

Sturges: log2(N) + 1. Scott / Freedman-Diaconis — автоматические. Часто 20-50 для аналитики.

Гистограмма или density plot?

Гистограмма для абсолютных частот. KDE (density plot) — сглаженный вариант.

Log scale или log-трансформация?

Log scale меняет только ось. Log-трансформация меняет данные (лучше для статистики).

Гистограмма для категорий?

Нет — для них countplot / barplot.


Тренируйте pandas — откройте тренажёр с 1500+ вопросами для собесов.