2025, Nov 12 12:02
Сопоставление нескольких столбцов в pandas одним кодом
Как сопоставить значения из нескольких столбцов в pandas и собрать единую метку без циклов: Series.map, fillna и backfill, объединение словарей через ChainMap.
Сопоставление разнородных столбцов единой нормализованной метке — типичная задача подготовки данных в pandas. Когда ожидается, что в каждой строке лишь одно поле совпадёт со справочной таблицей, наша цель — перевести значения из нескольких столбцов и собрать результат в одном новом столбце без разветвлённой логики и многословного кода.
Постановка задачи
Предположим, у нас есть набор данных с двумя кандидатными столбцами для классификации и числовым полем. В каждой строке значение только одного из этих двух столбцов встречается в заранее заданном отображении. Нужно перевести то значение, которое найдено в справочнике, и поместить код в единый столбец.
import pandas as pd
frame = pd.DataFrame({
'Event1': ['Music', 'Something else 1', 'Theatre', 'Comedy'],
'Event2': ['Something else 2', 'Ballet', 'Something else 3', 'Something else 4'],
'Cost': [10000, 5000, 15000, 2000]
})
code_map_a = {'Music': 'M', 'Cooking': 'C', 'Theatre': 'T', 'Comedy': 'C'}
code_map_b = {'Ballet': 'B', 'Swimming': 'S'}
Что здесь происходит и почему это непросто
Правила классификации зависят от столбца: значения в Event1 нужно расшифровывать по одной таблице соответствий, а в Event2 — по другой. Поскольку в строке ожидается совпадение лишь по одному столбцу, нам нужен аккуратный способ применить правильное сопоставление к каждому столбцу и взять первый успешный перевод. Реализация через построчные if/else быстро превращается в шум и легко упускает крайние случаи.
Краткое решение для фиксированного набора столбцов
Идиоматичный подход — переводить каждый столбец своим словарём, а затем с помощью fillna выбрать первое ненулевое (не NaN) совпадение. Так мы используем векторные операции и сохраняем декларативность преобразования.
frame['Event'] = frame['Event1'].map(code_map_a).fillna(
frame['Event2'].map(code_map_b)
)
Новый столбец Event будет содержать M, B, T или C для соответствующих строк — из того первого столбца, где нашлось валидное сопоставление.
Масштабирование на большее число столбцов с отдельными словарями
Если «событийных» столбцов становится больше, схема «столбец ↔ свой словарь» и выбор первого успешного перевода остаются простыми. Идея — собрать небольшой реестр соответствий «столбец → словарь», применить нужный справочник к каждому столбцу, затем выполнить backfill по столбцам и взять первое ненулевое значение.
code_map_c = {'Comedy': 'C'}
column_maps = {'Event1': code_map_a, 'Event2': code_map_b, 'Event3': code_map_c}
frame['Event'] = (
frame[list(column_maps)]
.apply(lambda col: col.map(column_maps[col.name]))
.bfill(axis=1)
.iloc[:, 0]
)
Этот приём предполагает, что отображения привязаны к конкретным столбцам. Он пробует правильный словарь для каждого столбца и объединяет результаты слева направо.
Глобальное сопоставление для всех «событийных» столбцов
Если один и тот же справочник должен применяться ко всем столбцам, удобно объединить несколько словарей в один и выполнить единое преобразование. После сопоставления выберите первое ненулевое значение в строке. Это можно оформить двумя равнозначными способами.
from collections import ChainMap
frame['Event'] = (
frame.filter(like='Event').stack()
.map(dict(ChainMap(code_map_a, code_map_b, code_map_c)))
.groupby(level=0).first()
)
Либо сделать векторный проход с последующим backfill:
from collections import ChainMap
frame['Event'] = (
frame.filter(like='Event')
.map(dict(ChainMap(code_map_a, code_map_b, code_map_c)).get)
.bfill(axis=1)
.iloc[:, 0]
)
Почему это важно
Такие паттерны позволяют держать логику, зависящую от столбцов, в декларативном виде и избегать построчных циклов и разовых условий. Они масштабируются от двух столбцов до многих без смены ментальной модели, остаются понятными на ревью и легко расширяются — достаточно добавить новую пару «столбец–словарь» или собрать общий справочник, когда бизнес‑правила сближаются.
Итоги
Когда в строке ожидается расшифровка только одного столбца, применяйте Series.map с соответствующим словарём на каждый столбец и используйте fillna, чтобы взять первое совпадение. Для больших схем соберите реестр «столбец → словарь» и сделайте backfill по преобразованным столбцам. Если правила едины для всех «событийных» полей, объедините словари и выбирайте первое найденное значение в строке. Так преобразование остаётся компактным, поддерживаемым и соответствует идиомам pandas.