Как бороться с SettingWithCopyWarning у панд?



фон



Я только что обновил свои панды с 0.11 до 0.13. 0rc1. Теперь, приложение выскакивает много новых предупреждений. Один из них такой:



E:FinReporterFM_EXT.py:449: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE


Я хочу знать, что именно это означает? Нужно ли мне что-то менять?



как я должен приостановить предупреждение, если я настаиваю на использовании quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE?



функция, которая дает ошибки



def _decode_stock_quote(list_of_150_stk_str):
"""decode the webpage and return dataframe"""

from cStringIO import StringIO

str_of_all = "".join(list_of_150_stk_str)

quote_df = pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
quote_df.rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
quote_df['TClose'] = quote_df['TPrice']
quote_df['RT'] = 100 * (quote_df['TPrice']/quote_df['TPCLOSE'] - 1)
quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
quote_df['TAmt'] = quote_df['TAmt']/TAMT_SCALE
quote_df['STK_ID'] = quote_df['STK'].str.slice(13,19)
quote_df['STK_Name'] = quote_df['STK'].str.slice(21,30)#.decode('gb2312')
quote_df['TDate'] = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])

return quote_df


больше сообщений об ошибках



E:FinReporterFM_EXT.py:449: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
E:FinReporterFM_EXT.py:450: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
quote_df['TAmt'] = quote_df['TAmt']/TAMT_SCALE
E:FinReporterFM_EXT.py:453: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
quote_df['TDate'] = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])
793   4  

4 ответов:

The SettingWithCopyWarning был создан, чтобы отметить потенциально запутанные" цепные " назначения, такие как следующие, которые не всегда работают должным образом, особенно когда первый выбор возвращает скопировать. [см.GH5390 и GH5597 для общего обсуждения.]

df[df['A'] > 2]['B'] = new_val  # new_val not set in df

предупреждение предлагает переписать следующим образом:

df.loc[df['A'] > 2, 'B'] = new_val

однако это не соответствует вашему использованию, что эквивалентно к:

df = df[df['A'] > 2]
df['B'] = new_val

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

pd.options.mode.chained_assignment = None  # default='warn'

в общем суть SettingWithCopyWarning это показать пользователям (и особенно новым пользователям), что они мая работать на копии, а не оригинал, как они думают. Там are ложные срабатывания (я знаю, что вы делаете, так что это ОК). Одна из возможностей-просто отключить (по умолчанию предупредить) предупреждение ,как @ Garrett предложить.

вот nother, за вариант.

In [1]: df = DataFrame(np.random.randn(5,2),columns=list('AB'))

In [2]: dfa = df.ix[:,[1,0]]

In [3]: dfa.is_copy
Out[3]: True

In [4]: dfa['A'] /= 2
/usr/local/bin/ipython:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  #!/usr/local/bin/python

вы можете установить is_copy флаг to False, что позволит эффективно отключить проверку, *для этого объекта"

In [5]: dfa.is_copy = False

In [6]: dfa['A'] /= 2

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

In [7]: dfa = df.ix[:,[1,0]].copy()

In [8]: dfa['A'] /= 2

код, который OP показывает выше, в то время как законный, и, вероятно, что-то я делаю, технически является случаем для этого предупреждения, а не ложным положительным. Еще один способ не имейте предупреждение было бы сделать операцию выбора через reindex, например,

quote_df = quote_df.reindex(columns=['STK',.......])

или

quote_df = quote_df.reindex(['STK',.......], axis=1) # v.0.21

Pandas dataframe copy warning

когда вы идете и делаете что-то вроде этого:

quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]

pandas.ixв этом случае возвращает новый, автономный фрейм данных.

любые значения, которые вы решили изменить в этом фрейме данных, не изменят исходный фрейм данных.

это то, о чем панды пытаются предупредить вас.

почему .ix - это плохая идея

The

чтобы устранить любые сомнения, мое решение состояло в том, чтобы сделать глубокую копию среза вместо обычной копии. Это может быть неприменимо в зависимости от вашего контекста (ограничения памяти / размер среза, потенциал для снижения производительности - особенно если копия происходит в цикле, как это было для меня, и т. д...)

чтобы быть ясным, вот предупреждение, которое я получил:

/opt/anaconda3/lib/python3.6/site-packages/ipykernel/__main__.py:54:
SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

иллюстрации

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

Пример 1: удаление столбца на оригинале влияет на копию

мы это уже знали, но это здоровое напоминание. Это не что предупреждение об этом.

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123


>> df2 = df1
>> df2

A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 affects df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    B
0   121
1   122
2   123

можно избежать изменений, внесенных в df1, чтобы повлиять на df2

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2
A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 does not affect df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    A   B
0   111 121
1   112 122
2   113 123

Пример 2: Удаление столбца на копии может повлиять на оригинал

Это на самом деле иллюстрирует предупреждение.

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> df2 = df1
>> df2

    A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df2 can affect df1
# No slice involved here, but I believe the principle remains the same?
# Let me know if not
>> df2.drop('A', axis=1, inplace=True)
>> df1

B
0   121
1   122
2   123

можно избежать изменений, внесенных в df2, чтобы повлиять на df1

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2

A   B
0   111 121
1   112 122
2   113 123

>> df2.drop('A', axis=1, inplace=True)
>> df1

A   B
0   111 121
1   112 122
2   113 123

Ура!

Comments

    Ничего не найдено.