2025, Nov 21 00:02
Частоты категорий: почему histplot в Seaborn равняет столбцы и что выбрать — barplot или countplot
Разбираем, почему в Seaborn histplot дает столбцы высотой 1 на предагрегированных данных, и чем его заменить: barplot для частот и countplot для сырых ответов
Построение частот категорий из заранее агрегированного списка: почему в histplot все столбцы равны 1 и как это исправить
Когда у вас есть длинный список ответов опроса — от A до I плюс пропуски как None — и вы хотите построить диаграмму частот в Seaborn, рука тянется к histplot. Подвох в том, что если сначала посчитать частоты и передать эту таблицу в histplot, все столбцы окажутся высотой 1. Оси выглядят правдоподобно, но смысл теряется из‑за неподходящего типа графика.
Демонстрация проблемы
Следующий фрагмент строит таблицу частот из списка значений с помощью Counter, а затем вызывает histplot. Визуализация получается с одинаковой высотой столбцов, потому что histplot ожидает сырые наблюдения и сам считает частоты.
import matplotlib.pyplot as mpl
from collections import Counter as Tally
import seaborn as sbn
import pandas as pnd
# Пример входных данных, имитирующий более длинный реальный список
survey_answers = ['E', None, 'B', 'H', 'I', 'A', None, None, 'D',
'C', 'E', 'I', 'C', 'B', None, 'A', None, 'E',
'F', 'H', 'A', 'D', 'A', 'A', 'F', 'A', 'C',
'C', 'H', 'E', None, 'B', 'E', 'I', 'G', 'A',
'I', 'A', 'B', 'I']
sbn.set_theme(style="darkgrid")
freq_map = pnd.DataFrame.from_dict(Tally(survey_answers), orient='index').reset_index()
freq_map.columns = ['Choice', 'Frequency']
# Неподходящий инструмент для заранее посчитанных частот: все столбцы будут высотой 1
sbn.histplot(data=freq_map, x='Choice')
mpl.show()Почему так происходит
histplot предназначен для того, чтобы агрегировать сырые данные за вас. Он бьёт значения на корзины или считает наблюдения по полученным значениям. В этой схеме вы уже свели данные к двухколоночной таблице категорий и их частот. Передавая такую предагрегированную таблицу в histplot, вы даёте Seaborn по одной строке на категорию, и она воспринимается как одно наблюдение — поэтому высота каждого столбца равна 1.
Правильный подход для заранее посчитанных частот
Используйте barplot для заранее вычисленных частот и отсортируйте индекс, чтобы сохранить категории в алфавитном порядке. Так вы явно сопоставляете категории с их счётчиками по оси y.
# Построить и отсортировать агрегированную таблицу по алфавиту по категории
chart_data = (
pnd.DataFrame.from_dict(Tally(survey_answers), orient='index')
.sort_index()
.reset_index()
)
chart_data.columns = ['Choice', 'Frequency']
# Правильный инструмент: barplot для заранее агрегированных данных
sbn.barplot(data=chart_data, x='Choice', y='Frequency')
mpl.show()Если вы не агрегируете заранее и у вас есть исходный список, вовсе не создавайте DataFrame — позвольте Seaborn посчитать всё через countplot.
# Считать напрямую из исходного списка
sbn.countplot(survey_answers)
mpl.show()Почему это различие важно
Выбор функции визуализации под состояние данных предотвращает тихие искажения. Предагрегированные данные стоит показывать столбчатой диаграммой, где категория отображается против счётчика; сырые наблюдения — передавать в функции, которые считают частоты внутри себя. Верный выбор удерживает ваш конвейер прозрачным, а график — верным данным.
Выводы
Если у вас уже есть таблица частот, используйте barplot и передавайте столбцы категории и частоты, при необходимости сортируя по индексу для алфавитного порядка. Если есть только исходный список ответов, countplot — самый прямой и надёжный путь. Короткая проверка на уровне мысли — «я даю построителю сырые наблюдения или сводные счётчики?» — убережёт от ловушки «все столбцы равны».