2025, Oct 22 14:01
pd.concat вместо update: добавляем строки и выравниваем столбцы в pandas
Разбираем, почему в pandas для добавления строк и выравнивания столбцов лучше использовать pd.concat, а не DataFrame.update. Пошаговый пример и советы по работе.
Когда два DataFrame в pandas совпадают по части столбцов, но не по всем, рука тянется к обновлению «на месте». Однако если задача — сохранить все строки первого DataFrame и добавить к ним строки из второго, выровненные по именам столбцов, update не спасёт. Здесь уместна построчная конкатенация с выравниванием столбцов.
Постановка задачи
Одна таблица шире (больше столбцов), другая — уже (подмножество этих столбцов, возможно, в другом порядке). Нужный результат: сохранить все исходные строки и дополнить их строками из узкой таблицы, помещая значения под совпадающими именами столбцов, а остальные оставляя пустыми.
Обманчивый подход
Ниже показан фрагмент, который пытается «обновить» один фрейм из другого по ключу. Он задаёт индекс и применяет DataFrame.update, а тот меняет значения только в уже существующих строках. Если второй DataFrame добавляет новые строки, а не правки, это не то, что нам нужно.
def sync_frames(big_tbl, small_tbl, id_col):
"""
Updates big_tbl with values from small_tbl based on a common key column.
Only columns present in small_tbl will be updated in big_tbl.
Parameters:
- big_tbl (pd.DataFrame): The larger DataFrame.
- small_tbl (pd.DataFrame): The smaller DataFrame with updated values.
- id_col (str): The column name used as the key for matching rows.
Returns:
- pd.DataFrame: Updated big_tbl.
"""
slim = small_tbl.drop_duplicates(subset=id_col)
big_tbl.set_index(id_col, inplace=True)
slim.set_index(id_col, inplace=True)
big_tbl.update(slim)
big_tbl.reset_index(inplace=True)
return big_tblПочему это не решает задачу
Ожидаемый результат предполагает, что строки второго DataFrame появятся в итоговой таблице как дополнительные, с выравниванием значений по одинаковым именам столбцов и пустотами в остальных. DataFrame.update не умеет добавлять строки: он лишь перезаписывает существующие ячейки там, где индексы пересекаются. Иначе говоря, вам нужен не «апдейт на месте», а объединение наборов данных по строкам.
Правильный подход: конкатенация по строкам с выравниванием столбцов
Чтобы добавить строки из второго DataFrame и сохранить выравнивание столбцов, используйте pd.concat. Он «складывает» строки и выравнивает столбцы по именам. Любой отсутствующий в конкретном входе столбец получит NaN (при желании можно заменить его на пустую строку для отображения).
import pandas as pd
# Объединяем строки с выравниванием столбцов
blended = pd.concat([wide_df, narrow_df], ignore_index=True, sort=False)
# Необязательно: отображать пропуски как пустые строки
blended = blended.fillna("")
# Необязательно: красиво вывести в виде таблицы Markdown
print(blended.to_markdown(tablefmt="grid"))В итоге сохраняются все строки из первого DataFrame, а строки второго добавляются следом. Общие столбцы выстраиваются корректно, в прочих — пустые значения там, где данных нет.
Почему это важно
Выбор между update и concat определяет, будете ли вы менять существующие строки или наращивать набор данных. В конвейерах, где сходятся разнородные источники или частичные выборки, конкатенация по строкам сохраняет целостность и предотвращает случайные перезаписи. Плюс она естественно справляется с разным порядком столбцов — выравнивание идёт по именам, а не по позициям.
Выводы
Если нужно добавить новые записи из DataFrame с подмножеством столбцов, используйте pd.concat с выравниванием столбцов (sort=False). Применяйте DataFrame.update только когда действительно хотите перезаписать значения в существующих строках по совпадающим индексам. А если результат смотрят люди, замените пропуски на пустую строку, чтобы таблица выглядела аккуратнее.
Статья основана на вопросе на StackOverflow от Anupkumar Kasi и ответе Mario.