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.