2025, Sep 22 12:01

Как объединить разрежённые строки по Name и Emp# в pandas с groupby и max

Разберём, как без pivot свернуть разрежённые Week1–Week4 в одну строку на сотрудника: правильный ключ группировки (Name+Emp#) в pandas и агрегатор max. Пример.

Когда данные о времени разбросаны по нескольким строкам на каждого сотрудника, последующая отчетность становится неудобной. Типичный случай — недельные итоги часов, где в каждой строке только одна неделя ненулевая, а остальные — нули. Цель — свернуть такие разреженные строки в одну запись на сотрудника, сохранив значения по неделям и не прибегая к самописной pivot-логике.

Постановка задачи

Предположим, у вас есть DataFrame pandas, где один и тот же сотрудник встречается в нескольких строках, и в каждой строке ровно одно из полей Week1–Week4 ненулевое. Нужно объединить все строки для одного человека в одну, сохранив ненулевые значения по неделям.

import pandas as pd

# 'records' — входной DataFrame со столбцами:
# 'Emp#', 'Name', 'Week1', 'Week2', 'Week3', 'Week4'
# Несколько строк на сотрудника; по неделям в основном нули, кроме одной ненулевой на строку.

Распространённая первая попытка — агрегировать только по имени, например так:

# Неверная группировка, если личность сотрудника определяется и Name, и Emp#
compact_wrong = records.groupby(['Name']).max()

Но такой подход не гарантирует нужную форму результата, если требуется сохранить и текстовое имя, и числовой идентификатор.

Что на самом деле вызывает проблему

Ключ группировки неполный. Группировка только по Name объединит все строки с одинаковым именем, игнорируя номер сотрудника. Если Name не является уникальным идентификатором или вы просто хотите сохранить Emp# в результате, одной группировки по Name недостаточно. Включайте в ключ и Name, и Emp#.

Есть и второй нюанс — выбор агрегатора. Поскольку нули играют роль заполнителей, а по каждой неделе у сотрудника максимум одно ненулевое значение, применение max по сгруппированным строкам поднимет это ненулевое значение по каждой неделе без какой-либо кастомной логики.

Решение

Сгруппируйте одновременно по Name и Emp# и используйте max, чтобы свернуть разреженные строки в одну запись на сотрудника.

# Верно: группировать по Name и Emp# и брать max, чтобы поднять ненулевые недельные значения
collapsed = records.groupby(['Name', 'Emp#']).max()

В результате получится компактная таблица: одна строка на сотрудника. Для иллюстрации, агрегированный вывод для приведённых данных выглядит так:

Name        Emp#    Week1   Week2   Week3   Week4
abc         6       0       45      0       45
anup        4       45      45      63      45
john        2       0       45      0       0
kumar       5       45      0       63      0
linda       3       45      0       63      0
mary        1       45      45      63      45

Если в изначальной «ожидаемой» таблице встречаются значения, которых нет в исходных строках, сперва проверьте источники. Несоответствия вроде неожиданных пропусков или чисел, отсутствующих во входных данных, не появятся после корректной агрегации через groupby.

Почему это важно

Подобные операции свёртки встречаются в учёте рабочего времени, биллинге и операционной аналитике. Правильный выбор ключей группировки критичен: пропустив один идентификатор, можно незаметно слить разные сущности. Не менее важен и агрегатор. В паттернах разреженных строк, где нули — заполнитель, а сигнал несёт единственное ненулевое значение, max — простой и точный способ извлечь этот сигнал по каждому столбцу.

Выводы

Используйте полный ключ группировки, отражающий уникальность сущности — здесь это и Name, и Emp#. Предпочитайте простые поколоночные агрегирования, соответствующие структуре данных: для разреженных недельных показателей max аккуратно поднимает ненулевые значения. Если итоговая агрегация выглядит неверно, сверьте исходные строки и «ожидаемые» значения, затем повторите groupby с правильными ключами.

Материал основан на вопросе на StackOverflow от Anupkumar Kasi и ответе Bending Rodriguez.