2025, Dec 25 21:01
Агрегирование пронумерованных диапазонов в pandas: wide_to_long и построчные min/max
Как получить для каждого name минимальный start и максимальный end в pandas. Два подхода: wide_to_long + groupby и построчные min/max. Примеры кода и объяснения
Когда один и тот же логический признак разнесён по пронумерованным столбцам, групповые агрегирования становятся непростыми. Типичный случай — по два диапазона в строке, например start_1/end_1 и start_2/end_2. Цель — для каждого name вычислить общий минимальный start и максимальный end по всем этим столбцам.
Постановка задачи
Предположим, у вас есть таблица, где у каждого name несколько диапазонов в широком формате. Нужно получить глобальные start и end для каждого name:
start_1 end_1 start_2 end_2 name
100 200 300 400 ABC
100 200 300 400 ABC
150 250 300 400 ABC
300 200 300 900 DEF
50 200 300 1000 DEF
Ожидаемый результат — по одной строке на name с минимальным start и максимальным end:
start end name
100 400 ABC
50 1000 DEF
Пример кода, воспроизводящий входные данные
Ниже фрагмент, который собирает ту же структуру, чтобы вы могли воспроизвести задачу целиком:
import pandas as pd
data_map = {
'start_1': [100, 100, 150, 300, 50],
'end_1': [200, 200, 250, 200, 200],
'start_2': [300, 300, 300, 300, 300],
'end_2': [400, 400, 400, 900, 1000],
'name': ['ABC', 'ABC', 'ABC', 'DEF', 'DEF']
}
frame = pd.DataFrame(data_map)
Что здесь происходит на самом деле
Данные представлены в широком виде: две пары столбцов описывают два диапазона в каждой строке. Чтобы корректно агрегировать по name, можно либо преобразовать таблицу в длинный формат, выровняв все start и end в одних и тех же столбцах, либо вычислить построчно минимум по всем столбцам start_* и максимум по всем столбцам end_*, а затем сгруппировать по name.
Решение 1: преобразовать с wide_to_long и затем выполнить groupby-agg
Перевод пронумерованных столбцов в длинный формат делает агрегацию простой. После преобразования сгруппируйте по name и возьмите min для start и max для end — получится нужный результат.
result_set = (
pd.wide_to_long(
frame.reset_index(),
stubnames=['start', 'end'],
i=['index', 'name'],
j=' ',
sep='_'
)
.groupby('name')
.agg(start=('start', 'min'), end=('end', 'max'))
.reset_index()
)
print(result_set)
Вывод:
name start end
0 ABC 100 400
1 DEF 50 1000
Почему это работает
После преобразования каждая исходная строка даёт две записи: одну для набора «_1» и одну для «_2». В итоге получаем аккуратную таблицу start и end, выровненную по name:
print(
pd.wide_to_long(
frame.reset_index(),
stubnames=['start', 'end'],
i=['index', 'name'],
j=' ',
sep='_'
)
)
start end
index name
0 ABC 1 100 200
2 300 400
1 ABC 1 100 200
2 300 400
2 ABC 1 150 250
2 300 400
3 DEF 1 300 200
2 300 900
4 DEF 1 50 200
2 300 1000
Решение 2: посчитать построчные min/max и затем агрегировать
Другой подход — добавить в каждую строку два вспомогательных столбца: минимальный среди всех start_* и максимальный среди всех end_*. После этого сгруппируйте по name, взяв min для start и max для end — получите тот же результат.
final_view = (
frame.assign(
start=frame.filter(like='start').min(axis=1),
end=frame.filter(like='end').max(axis=1)
)
.groupby('name')
.agg(start=('start', 'min'), end=('end', 'max'))
.reset_index()
)
print(final_view)
Результат совпадает с целью:
name start end
0 ABC 100 400
1 DEF 50 1000
Чтобы увидеть построчные вспомогательные значения до группировки:
print(
frame.assign(
start=frame.filter(like='start').min(axis=1),
end=frame.filter(like='end').max(axis=1)
)
)
start_1 end_1 start_2 end_2 name start end
0 100 200 300 400 ABC 100 400
1 100 200 300 400 ABC 100 400
2 150 250 300 400 ABC 150 400
3 300 200 300 900 DEF 300 900
4 50 200 300 1000 DEF 50 1000
Зачем это знать
Реальные датасеты часто приходят в широком формате с пронумерованными полями. Быстро нормализуя такие структуры, вы получаете возможность просто применять groupby-операции. В pandas выбор между преобразованием в длинный вид и вычислением построчной статистики зависит от того, нужно ли вам сохранять/переиспользовать длинную форму для дальнейшего анализа. Оба шаблона лаконичны и хорошо масштабируются на похожие случаи с несколькими суффиксами.
Вывод
Если нужно получить по группам минимумы и максимумы по пронумерованным столбцам start/end, преобразование через wide_to_long с последующей агрегацией groupby даёт чистый и наглядный конвейер. Когда важен лишь итоговый результат по группам, а имена столбцов следуют единой конвенции, вычисление построчных min и max с помощью filter(like=...) и последующая агрегация столь же эффективны. Выберите путь, соответствующий вашему рабочему процессу, и держите суффиксы столбцов единообразными — так преобразования будут предсказуемыми.