2025, Sep 25 05:16
Как в pandas отобрать имена с одним классом и посчитать частоты
Показываем, как в pandas за один проход получить имена с ровно одним классом и их частоты: groupby + agg с nunique и count. Короткий масштабируемый шаблон кода.
Когда нужно найти все имена, которым соответствует ровно одно значение в категориальном столбце, и одновременно показать, сколько раз встречается каждая пара имя–класс, самый прямой способ в pandas — агрегировать и отфильтровать данные за один проход. Ниже — лаконичный шаблон, который избавляет от ручной постобработки и возвращает и само значение, и его частоту.
Постановка задачи
У нас есть набор данных с повторяющимися именами и их классами. Нужно вернуть только те имена, у которых класс единственный во всех их строках, и добавить столбец с числом вхождений этой пары имя–класс. Имя, встречающееся один раз, тоже считается уникальным.
Воспроизводимый пример
Код ниже строит DataFrame и показывает первую попытку — перечислить уникальные классы для каждого имени:
import pandas as pd
records = {
    'name':  ['Nick', 'Jane', 'Jacon', 'Jack', 'Cooze', 'Nick', 'Jane', 'Jacon', 'Jack', 'Cooze', 'John'],
    'class': ['a',    'b',     'a',    'b',    'a',     'b',    'b',    'c',     'a',    'a',     'a']
}
frame = pd.DataFrame(records)
by_name_unique = frame.groupby('name')['class'].unique()
print(by_name_unique)
Это даёт набор уникальных значений класса для каждого имени, но дальше придётся вручную считать длины и фильтровать — лишняя и потенциально ошибочная работа.
Что здесь происходит на самом деле
Вызов .unique() возвращает массивы различных значений по каждой группе. Чтобы понять, есть ли у имени ровно один класс, важно посчитать, сколько разных классов у него встречается. Эту задачу как раз решает .nunique(). Кроме того, нам нужна частота для пары имя–класс, а это просто .count() в той же группе. Объединив эти агрегации, мы избегаем лишних проходов и неудобных проверок длины массивов.
Решение
Агрегируем, фильтруем по числу разных классов и оставляем нужные столбцы. Фрагмент ниже возвращает только те имена, у которых один-единственный класс, вместе с самим значением класса и его частотой. Имена, встречающиеся один раз, попадают в результат, поскольку число разных классов у них равно 1.
result = (
    frame.groupby('name')['class']
         .agg([('class', 'first'), 'nunique', 'count'])
         .query('nunique == 1')
         .drop(columns='nunique')
)
print(result)
Ожидаемый вывод для данных выше показывает записи, где у каждого имени ровно один класс, а также сколько раз оно встречается:
       class  count
name                
Cooze      a      2
Jane       b      2
John       a      1
Почему это важно
Опора на .nunique() и .agg() и понятнее, и быстрее, чем собирать массивы и потом измерять их длину. Логика остаётся декларативной: один раз сгруппировали, посчитали ровно то, что нужно, отфильтровали и показали результат. Такой подход лучше масштабируется на больших данных, поскольку избегает циклов на уровне Python и работы со списками, позволяя pandas выполнять векторизованные операции «под капотом».
Итог
Когда в pandas нужно одновременно проверить «уникальность внутри группы» и посчитать количество, не создавайте промежуточные массивы через .unique(). Сразу используйте .groupby(...).agg(...): nunique — для проверки уникальности, count — для частоты; фильтруйте по nunique == 1 и оставляйте интересующие столбцы. Решение получается коротким, прозрачным и устойчивым к случаям, когда имена могут повторяться с разными значениями класса.