May 2, 2022

Откуда возникает самое распространенное предупреждение Pandas, и как его обойти

Имя этому предупреждению - SettingWithCopyWarning. Оно вызывается из-за возможной проблемы при модификации данных, когда либо изменения вообще не произойдут, либо появятся там, где вы можете их не ожидать. Рассмотрим простой датафрейм:

import pandas as pd
df1 = pd.DataFrame([[1,2,3], [4,5,6], [7,8,9], [10,11,12]],
                  columns=['first', 'second', 'third'])
df1

При различных способах обращения к датафрейму или его части может возвращаться либо представление (при модифицикации будет затронут датафрейм), либо копия данных (тогда изменения затронут только ее). Например, при таком обращении будет возвращена копия, и датафрейм модифицирован не будет

df1[df1['first'] > 3]['first'] = 1
df1

в то же время, если обратиться так, то изменения в первоисточнике появятся:

df1['first'][df1['first'] > 3] = 3
df1

Однако рекомендованным способом внесения изменений в срез датафрейма является с использованием оператора loc. Пересоздадим таблицу и применим данный способ:

df1 = pd.DataFrame([[1,2,3], [4,5,6], [7,8,9], [10,11,12]],
                  columns=['first', 'second', 'third'])
df1.loc[df1['first']>3, 'first'] = 3
df1

Вместе с тем большая доля предупреждений приходится при модификации ранее созданной ссылки или копии на датафрейм. Например, при указанном ниже способе возвращается копия и изменения не затронут первоисточник:

df2 = df1[['first']]
df2['first'] /= 2

display(df2)
display(df1)

С использованием же оператора loc вы гарантируете, что работаете со срезом первоисточника:

df2 = df1.loc[:,['first']]
df2['first'] /= 2
display(df2)
display(df1)

Если же вы хотите работать с копией и обезопасить себя от изменения первоисточника явно добавляйте метод copy при создании новой переменной, даже когда это происходит внутри функции. Например, часто предупреждение возникает при создании выборок из данных посредством train_test_split:

from sklearn.model_selection import train_test_split

feat_tr, feat_ts = train_test_split(df1, test_size=0.5) 
feat_tr['first'] = feat_tr['first']**2
display(feat_tr)
display(df1)

Несмотря на то, что функцией возвращается копия, вылетает предупреждение. Если же вы явно передадите в train_test_split параметр df1.copy(), оно не возникнет:

from sklearn.model_selection import train_test_split

feat_tr, feat_ts = train_test_split(df1.copy(), test_size=0.5) 
feat_tr['first'] = feat_tr['first']**2
display(feat_tr)
display(df1)

Более грубым способом обхода предупреждения является его запрет:

pd.options.mode.chained_assignment = None
from sklearn.model_selection import train_test_split

feat_tr, feat_ts = train_test_split(df1, test_size=0.5) 
feat_tr['first'] = feat_tr['first']**2
display(feat_tr)
display(df1)

или вообще запрет всех предупреждений:

import warnings
warnings.filterwarnings('ignore')

Последние два способа не рекомендую: лучше бороться с причиной, нежели закрывать на нее глаза.

Не пропустите ничего интересного и подписывайтесь на страницы канала в других социальных сетях:

Яндекс Дзен

Telegram