Время прочтения: 3 мин.

Если вы работаете с библиотекой pandas, то велика вероятность что вы сталкивались с предупреждением “SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame”. Иногда данное предупреждение можно игнорировать, и выполнение кода будет правильным. Однако, необходимо быть очень осторожным и понимать, что означает это предупреждение. Расплатой могут быть потраченные впустую часы работы, когда вместо ожидаемого измененного датафрейма, вы получаете старые значения. В этом посте я расскажу, что означает данное предупреждение и как правильно присваивать значения внутри датафрейма.

Основная идея этого предупреждения связана с понятием view (представление) и copy (копия). Одни операции возвращают копию, а другие представление. Представление – это подмножество изначального объекта, связанное с ним ссылкой. При изменении представления мы изменяем оригинальный объект. Копия – совершенно новый объект. Изменение копии никак не отражается на оригинальном объекте, так как они не связаны между собой.

Создадим в качестве примера следующий датафрейм:

Пусть нам необходимо для всех строк, где столбец Y больше 13, в столбце Z присвоить 777. Первое, что приходит в голову записать это в виде:

df[df['Y'] > 13]['Z'] = 777

Однако после выполнения мы можем заметить, что ничего в исходном датафрейме не поменялось.

Происходит это потому, что такая запись выполняется как две операции: фильтрация и присваивание. Фильтрация возвращает копию. Соответственно присваивание применяется уже не к исходному объекту. Для того чтобы избежать данной ошибки, необходимо исключить использование конструкции [<фильтр>][<столбец>]. Вместо неё можно использовать функцию loc[<фильтр>, <столбец>] или поменять местами операции [<столбец>][<фильтр>] (в этом случае выбор столбца возвращает series, ссылающаяся на оригинальный столбец, а фильтрация с присваиванием изменяет эти значения).

Неправильно: df[df['Y'] > 13]['Z'] = 777
Неправильно: df.loc[df['Y'] > 13]['Z'] = 777
Правильно: df.loc[df['Y'] > 13, 'Z'] = 777
Правильно: df.['Z'][df['Y'] > 13] = 777

Говоря в целом об операциях выбора столбцов, фильтрации и присваивании, нежелательно использовать сложные конструкции с цепочками этих операций. В документации Pandas указано, что использование сложных конструкций зачастую непредсказуемо, может в одинаковых случаях возвращать или view или copy (в зависимости о расположения массива в памяти). Поэтому чтобы избежать ошибок лучше использовать простые выражения с использованием одной операции фильтрации.