value_counts() в pandas — частоты, доли и распределения
"b" in ["a", "b", "c"]?Содержание:
Зачем аналитику value_counts
Когда вы впервые открываете незнакомый датасет, первый вопрос почти всегда один: «а что вообще лежит в этом столбце?». Сколько уникальных городов, какие сегменты пользователей преобладают, нет ли подозрительного значения вроде пустой строки или unknown, которое съедает половину выборки. Руками это считать бессмысленно, а строить полноценную гистограмму ради быстрого взгляда — долго. Именно для этого в pandas есть value_counts() — метод, который за одну строку выдаёт частоту каждого уникального значения и сразу сортирует по убыванию.
На собеседованиях на аналитика value_counts() всплывает постоянно в задачах на EDA (exploratory data analysis): «как быстро посмотреть распределение», «найди топ-5 категорий», «посчитай долю каждого сегмента». Метод выглядит простым, но за normalize, bins и обработкой NaN прячется несколько нюансов, на которых легко споткнуться. Разберём их по порядку — с реальным кодом и выводом, который вы получите в pandas 2.x.
Что делает value_counts
value_counts() — это метод Series. Он берёт столбец, считает, сколько раз встречается каждое уникальное значение, и возвращает новый Series: в индексе — сами значения, в значениях — их количество. По умолчанию результат отсортирован по убыванию частоты, поэтому самое частое значение всегда сверху.
import pandas as pd
df = pd.DataFrame({
'city': ['Москва', 'Питер', 'Москва', 'Казань', 'Москва', 'Питер', 'Казань', 'Москва'],
'segment': ['premium', 'free', 'premium', 'free', 'free', 'premium', 'free', 'premium'],
})
df['city'].value_counts()
# city
# Москва 4
# Питер 2
# Казань 2
# Name: count, dtype: int64Обратите внимание на подпись Name: count — начиная с pandas 2.0 результирующий Series называется count (а с normalize=True — proportion). В старых статьях и ответах на Stack Overflow вы встретите Name: city, dtype: int64 — это устаревший формат, на свежей версии он выглядит уже иначе.
Параметры value_counts
У метода пять параметров, которые покрывают почти все задачи частотного анализа: normalize, sort, ascending, dropna и bins. Ниже — каждый с реальным выводом.
normalize — доли вместо количества
normalize=True возвращает не абсолютные числа, а доли от общего количества (сумма всех значений равна единице). Это удобно, когда важна не штука, а процент.
df['city'].value_counts(normalize=True)
# city
# Москва 0.50
# Питер 0.25
# Казань 0.25
# Name: proportion, dtype: float64
# В процентах
(df['city'].value_counts(normalize=True) * 100).round(1)
# city
# Москва 50.0
# Питер 25.0
# Казань 25.0
# Name: proportion, dtype: float64sort и ascending — порядок строк
По умолчанию sort=True — pandas сортирует по частоте по убыванию. Параметр ascending=True переворачивает порядок: реже встречающиеся значения окажутся сверху. А если нужна сортировка по самому значению (по алфавиту или по датам), частотную сортировку отключают через .sort_index().
# По возрастанию частоты
df['city'].value_counts(ascending=True)
# city
# Питер 2
# Казань 2
# Москва 4
# Name: count, dtype: int64
# По индексу (алфавит), а не по частоте
df['city'].value_counts().sort_index()
# city
# Казань 2
# Москва 4
# Питер 2
# Name: count, dtype: int64dropna — учёт пропусков
По умолчанию value_counts() молча выбрасывает пропуски: NaN в результат не попадает. Это удобно для анализа категорий, но опасно, если вы оцениваете качество данных — пропуски просто исчезнут из поля зрения. Параметр dropna=False включает NaN в подсчёт отдельной строкой.
import numpy as np
s = pd.Series(['A', 'B', 'A', np.nan, 'B', np.nan, 'A'])
s.value_counts() # NaN не считается (по умолчанию)
# A 3
# B 2
# Name: count, dtype: int64
s.value_counts(dropna=False) # NaN учитывается
# A 3
# B 2
# NaN 2
# Name: count, dtype: int64bins — группировка числовых данных
Для числового столбца считать частоту каждого уникального числа обычно бессмысленно: при тысячах разных значений вы получите тысячи строк по единице. Параметр bins=N сначала режет диапазон на N равных интервалов, а потом считает, сколько значений попало в каждый — фактически быстрая гистограмма без matplotlib.
ages = pd.Series([22, 25, 31, 28, 45, 37, 19, 55, 42, 33])
ages.value_counts(bins=4).sort_index()
# (18.963, 28.0] 4
# (28.0, 37.0] 3
# (37.0, 46.0] 2
# (46.0, 55.0] 1
# Name: count, dtype: int64Левая граница первого интервала чуть ниже минимума (18.963, а не 19) — pandas намеренно расширяет её, чтобы включить крайнее значение. Без .sort_index() строки идут по убыванию частоты, что для интервалов читается хуже, поэтому интервалы почти всегда сортируют по индексу.
value_counts по нескольким столбцам
С pandas 1.1 value_counts() работает не только на Series, но и на DataFrame — тогда он считает частоту комбинаций значений всех переданных столбцов. Это быстрый способ получить кросс-табуляцию «какие пары встречаются и сколько раз».
df[['city', 'segment']].value_counts()
# city segment
# Москва premium 3
# Казань free 2
# Москва free 1
# Питер free 1
# premium 1
# Name: count, dtype: int64Результат — Series с MultiIndex (город + сегмент), снова отсортированный по убыванию частоты. Если вам важнее не частота пары, а отдельная агрегация, тот же результат даёт df.groupby(['city', 'segment']).size() — разница в том, что groupby по умолчанию сортирует по ключам, а не по частоте. О сравнении этих двух подходов — ниже.
value_counts vs groupby и SQL GROUP BY
value_counts() — это, по сути, синтаксический сахар поверх группировки. Эквивалент через groupby — df.groupby('city').size(), только без автоматической сортировки по частоте и без normalize.
df.groupby('city').size()
# city
# Казань 2
# Москва 4
# Питер 2
# dtype: int64
# Чтобы получить тот же порядок, что у value_counts:
df.groupby('city').size().sort_values(ascending=False)Правило выбора простое: если нужна частота одного столбца с сортировкой и долями — берите value_counts(), он короче и читается понятнее. Если рядом с подсчётом нужна агрегация других колонок (средний чек, сумма, несколько метрик сразу) — это уже работа для groupby().agg(), потому что value_counts() умеет только считать строки.
| Задача | value_counts | groupby |
|---|---|---|
| Частота одного столбца | df['col'].value_counts() |
df.groupby('col').size() |
| Доли | normalize=True |
вручную .div(len(df)) |
| Сортировка по частоте | по умолчанию | .sort_values() |
| Бины для чисел | bins=N |
pd.cut() + groupby |
| Агрегация других колонок | нет | .agg(...) |
В SQL прямой аналог value_counts() — это GROUP BY с COUNT(*) и сортировкой. Если вы пришли в pandas из SQL (или наоборот), держите в голове эту параллель — на собесе её часто просят проговорить.
-- Аналог df['city'].value_counts()
SELECT city, COUNT(*) AS cnt
FROM users
GROUP BY city
ORDER BY cnt DESC;
-- Аналог value_counts(normalize=True) — доли
SELECT city,
COUNT(*)::NUMERIC / SUM(COUNT(*)) OVER () AS share
FROM users
GROUP BY city
ORDER BY share DESC;Заметьте ::NUMERIC во втором запросе: без приведения типа целочисленное деление в PostgreSQL усечёт результат до нуля. Подробнее про группировку в базе — в гайде по GROUP BY.
Частые задачи на собесе
На интервью value_counts() обычно встречается в трёх формулировках. Первая — топ-N категорий. Поскольку метод уже сортирует по убыванию, достаточно дописать .head(N).
df['city'].value_counts().head(2)
# city
# Москва 4
# Питер 2
# Name: count, dtype: int64Вторая — доля каждой категории. Здесь спасает normalize=True; если просят именно проценты, умножаем на 100. Удобно сразу прогнать пару таких задач в тренажёре Карьерника, чтобы синтаксис не вылетал из головы на собесе.
segment = pd.Series(['free']*12 + ['premium']*5 + ['trial']*3)
(segment.value_counts(normalize=True) * 100).round(1)
# free 60.0
# premium 25.0
# trial 15.0
# Name: proportion, dtype: float64Третья — фильтрация редких категорий. Частоты сами по себе можно использовать как маску: оставить только популярные значения или, наоборот, отсечь хвост из единичных категорий перед обучением модели.
cat = pd.Series(['x','x','x','y','z','x','y','x','x','y','q'])
counts = cat.value_counts()
# Категории, которые встречаются реже 2 раз
rare = counts[counts < 2].index
filtered = cat[~cat.isin(rare)]
# редкие значения 'z' и 'q' выброшеныЕщё одна классика — найти самое частое значение (моду). Поскольку value_counts() сортирует по убыванию, первый элемент индекса и есть мода — то же самое вернёт .mode().
df['city'].value_counts().index[0] # 'Москва'
df['city'].mode()[0] # 'Москва'Частые ошибки
Путают value_counts() с count(). value_counts() считает частоту каждого значения, а df['col'].count() — просто число непустых ячеек. Это разные вопросы: «сколько каких» против «сколько всего».
Забывают про NaN. По умолчанию пропуски выпадают из подсчёта. Если вы оцениваете долю заполненности столбца через value_counts() без dropna=False, вы получите красивую картину, которая врёт — потому что пропуски просто не показаны. Для аудита данных всегда добавляйте dropna=False.
Считают проценты вручную, когда есть normalize. Конструкция df['col'].value_counts() / len(df) * 100 работает, но value_counts(normalize=True) короче и не сломается, если в столбце есть пропуски (нормализация считается от непустых значений, а не от длины DataFrame).
Вызывают value_counts() на числовом столбце без bins. Для непрерывной величины (доход, возраст, время на сайте) вы получите простыню из тысяч строк по единице. Для таких столбцов нужен bins=N или pd.cut().
Сортируют по индексу, забыв sort_index(). value_counts() всегда сортирует по частоте. Если нужен порядок по самому значению (алфавит, даты, числовые интервалы), дописывайте .sort_index() — иначе строки пойдут хаотично с точки зрения значений.
Связанные темы
- groupby в pandas
- pandas vs SQL: что выбрать
- Сортировка DataFrame: sort_values
- Кросс-табуляция: crosstab в pandas
- Вопросы по pandas на собеседовании
FAQ
Чем value_counts отличается от groupby + count?
value_counts() — это шорткат для частоты одного столбца: сразу сортирует по убыванию и умеет normalize. groupby().size() даёт тот же подсчёт, но без сортировки по частоте и без долей, зато позволяет считать агрегации по другим колонкам. Для «сколько каких значений» берите value_counts(), для сложной агрегации — groupby().agg().
Как посчитать процент каждой категории?
Через df['col'].value_counts(normalize=True) * 100. Метод вернёт доли (сумма равна единице), умножение на 100 переводит их в проценты. Для округления допишите .round(1).
Можно ли применять value_counts к числовому столбцу?
Можно, но для непрерывных данных используйте bins=N — иначе каждое уникальное число станет отдельной строкой. С bins pandas сам разобьёт диапазон на интервалы и посчитает частоту попаданий в каждый, как в гистограмме.
Как сделать value_counts сразу по нескольким столбцам?
Передайте DataFrame с нужными колонками: df[['col1', 'col2']].value_counts(). Метод посчитает частоту комбинаций значений и вернёт Series с MultiIndex. Это работает с pandas 1.1 и новее.
Какой аналог value_counts в SQL?
SELECT col, COUNT(*) AS cnt FROM table GROUP BY col ORDER BY cnt DESC — точный аналог df['col'].value_counts(). Для долей добавьте оконную функцию COUNT(*)::NUMERIC / SUM(COUNT(*)) OVER (), не забыв привести тип, чтобы не словить целочисленное деление.
Почему у value_counts в выводе написано Name: count?
С pandas 2.0 результирующий Series называется count (а при normalize=True — proportion). Раньше он наследовал имя столбца. Если вы видите в старых примерах Name: city, dtype: int64, это просто более ранняя версия pandas — на поведение метода смена имени не влияет.