2025, Nov 01 04:16

Как избежать FutureWarning в pandas 2: используем combine_first вместо построчных присваиваний

Статья объясняет, почему в pandas 2 появляется FutureWarning при присваивании в столбцы с NaN и как решить это через combine_first, избежав проблем с dtype.

Код, который безобидно работал в pandas 1.x, в pandas 2 начинает выдавать предупреждения, если смешивать неизвестные dtypes и построчные присваивания. Типичный сценарий — заранее создать столбец, заполненный NaN, а затем пытаться перезаписать отдельные строки значениями из другого DataFrame, при этом заранее не зная dtype соответствующего столбца. В итоге появляется FutureWarning о несовместимом dtype, который в будущих версиях превратится в ошибку.

Как воспроизвести проблему

Ниже пример, точно повторяющий ситуацию: к первому DataFrame добавляют новый столбец с NaN, после чего строки присваиваются из другого DataFrame. В pandas 2 это приводит к FutureWarning о несовместимом dtype.

import pandas as pd
import numpy as np

base_df = pd.DataFrame({"i": [1, 2, 3, 4, 5], "a": [2, 4, 6, 8, 10]})
patch_df = pd.DataFrame({"i": [2, 4], "a": [3, 6], "b": [4, 8]})

base_df["b"] = np.nan
base_df.loc[patch_df.index, :] = patch_df

При этом выводится предупреждение:

FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas.

Что происходит

Присваивание пытается поместить значения из одного DataFrame в другой, где целевой столбец изначально заполнен NaN. Если dtype входного столбца заранее не известен, нет гарантии, что тип заранее созданного столбца сможет корректно представить эти значения без апкаста или потери информации. В pandas 2 это явно обозначается FutureWarning — сигналом, что в будущем тихое приведение типов допускаться не будет. Обычно помогает привести столбец к типу входящих данных, но здесь dtype заранее неизвестен и может не поддерживать NaN, поэтому надежно «предкастовать» его нельзя.

Решение: объединять через combine_first

Вместо предварительного создания столбца и построчных присваиваний объедините два DataFrame так, чтобы значения из «заплаты» имели приоритет там, где они есть. Такой подход избавляет от хрупких манипуляций с типами и корректно обращается с пропущенными значениями.

import pandas as pd

# пример
base_df = pd.DataFrame({"i": [1, 2, 3, 4, 5], "a": [2, 4, 6, 8, 10]})
patch_df = pd.DataFrame({"i": [2, 4], "a": [3, 6], "b": [4, 8]})

# решение
merged_df = patch_df.combine_first(base_df)[patch_df.columns]

Результирующий DataFrame ведет себя как ожидается: «заплата» применена, пропуски сохранены:

   i   a    b
0  2   3  4.0
1  4   6  8.0
2  3   6  NaN
3  4   8  NaN
4  5  10  NaN

Почему это важно

Код, опирающийся на неявное приведение типов, начнет ломаться по мере ужесточения правил работы с dtype в pandas. Замена построчных присваиваний в столбцы, заранее заполненные NaN, на схему объединения через combine_first избавляет от зависимости от неизвестных dtypes и предотвращает будущие ошибки. Плюс намерение становится очевидным: где есть значения из «заплаты», берем их, иначе сохраняем исходные.

Что запомнить

Если вы не контролируете и не можете предсказать входной dtype, не выделяйте столбцы заранее с NaN и не делайте построчных присваиваний. Объединяйте через combine_first — пусть pandas сам разрулит выравнивание и пропуски. И обязательно читайте полные тексты предупреждений: они прямо указывают на изменения, которые в следующих версиях превратятся в жесткие ошибки.

Материал основан на вопросе на StackOverflow от guest и ответе Panda Kim.