2025, Oct 18 12:16
Как получить бинарную индикаторную матрицу в Pandas без циклов
В Pandas показываем, как из строк целых чисел получить бинарную индикаторную матрицу без циклов: stack + one‑hot через get_dummies и транспонирование.
Преобразование строк из целых чисел в бинарную индикаторную матрицу на первый взгляд похоже на one‑hot‑кодирование, но цель здесь другая: для каждой исходной строки нам нужен отдельный бинарный столбец, который помечает позиции (значения индекса), присутствующие в этой строке, а в остальных местах остаются нули. Сложность в том, чтобы сделать это для множества строк сразу и без циклов.
Пример и наивная попытка
Рассмотрим простой DataFrame Pandas, где в каждой строке перечислены позиции (целые числа), которые нужно отметить:
import pandas as pd
data = pd.DataFrame({
"A": [1, 2, 3],
"B": [4, 5, 6],
"C": [7, 8, 9],
}, index=[0, 1, 2])
Простой вариант для одной строки — построить диапазон всех возможных позиций и проверить вхождение относительно этой строки. Для единичного случая это работает, но не масштабируется на весь фрейм:
src_df = data
out_grid = pd.DataFrame(range(src_df.max(axis=None) + 1)).isin(list(src_df.iloc[0]))
Этот подход обрабатывает только первую строку и не распространяется на все строки одновременно.
Что на самом деле происходит
Нужная нам структура проявляется, если сменить точку зрения. Если развернуть «широкую» таблицу в длинный столбец, можно one‑hot‑закодировать целочисленные значения, а затем агрегировать по исходным строкам. Так сохраняются позиции, и становится просто собрать бинарную индикаторную матрицу для каждой строки за один проход.
Векторизованное решение в Pandas
Вот компактный, полностью векторизованный конвейер, который выполняет преобразование от начала до конца:
result = pd.get_dummies(data.stack()).groupby(level=0).sum().T
Он выполняется в четыре шага. Сначала stack преобразует данные в длинный Series с многоуровневым индексом из исходного индекса строк и прежних названий столбцов. Затем get_dummies превращает целочисленные значения в one‑hot‑столбцы. Далее groupby(level=0).sum() агрегирует эти индикаторы по исходным строкам (первый уровень MultiIndex). Наконец, .T транспонирует таблицу так, что позиции становятся индексом строк, а исходные строки — столбцами.
Получившийся результат соответствует требуемой бинарной разметке:
0 1 2
1 1 0 0
2 0 1 0
3 0 0 1
4 1 0 0
5 0 1 0
6 0 0 1
7 1 0 0
8 0 1 0
9 0 0 1
Заглянем под капот
Если хотите увидеть промежуточные объекты для ясности, разбейте решение на именованные шаги. Так проще проверить, как данные проходят через конвейер, не прибегая к циклам:
s_long = data.stack()
ohe = pd.get_dummies(s_long)
agg = ohe.groupby(level=0).sum()
final = agg.T
Длинный ряд, полученный с помощью stack, связывает исходный индекс строки с прежним именем столбца. get_dummies переводит целочисленные значения в индикаторные столбцы. Группировка по первому уровню индекса суммирует индикаторы в пределах каждой исходной строки. Транспонирование поворачивает таблицу в нужную ориентацию: позиции становятся индексом.
Почему это важно
Переформатирование, а затем one‑hot‑кодирование избавляют от кастомных циклов и построчной логики, оставаясь в рамках идиоматичного Pandas. Преобразование легко читать, удобно отлаживать и применяется к любому числу строк. Если вы думали о for‑цикле или .apply() по строкам, этот шаблон снимает такую необходимость. А числовой индекс делает операции предсказуемыми и понятными.
Вывод
Когда нужна бинарная индикаторная матрица, которая отмечает целочисленные позиции из каждой строки, разверните данные в длинный формат с помощью stack, выполните one‑hot‑кодирование через get_dummies, агрегируйте по исходным строкам и транспонируйте. Одна строка pd.get_dummies(data.stack()).groupby(level=0).sum().T решает задачу в векторизованном стиле и естественно масштабируется на весь DataFrame.
Статья основана на вопросе на StackOverflow от lane-h-rogers и ответе PaulS.