Как бороться с SettingWithCopyWarning в Pandas?
Фон
Я только что обновил свой Pandas от 0.11 до 0.13.0rc1. Теперь приложение выдает много новых предупреждений. Один из них:
E:\FinReporter\FM_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:\FinReporter\FM_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:\FinReporter\FM_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:\FinReporter\FM_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])
Ответы
Ответ 1
SettingWithCopyWarning
был создан для 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'
Ответ 2
В общем, смысл SettingWithCopyWarning
- показать пользователям (и особенно новым пользователям), что они могут работать с копией, а не с оригиналом, как они думают. Есть ложные срабатывания (IOW, если вы знаете, что делаете, может быть в порядке). Одна из возможностей - просто отключить (по умолчанию предупреждение) предупреждение, как предлагает @Garrett.
Вот еще один вариант:
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
в 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
Ответ 3
Как бороться с SettingWithCopyWarning
в Pandas?
Этот пост предназначен для читателей, которые,
- Хотелось бы понять, что означает это предупреждение
- Хотелось бы понять разные способы подавления этого предупреждения
- Хотелось бы понять, как улучшить свой код и следовать передовой практике, чтобы избежать этого предупреждения в будущем.
Настроить
np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (3, 5)), columns=list('ABCDE'))
df
A B C D E
0 5 0 3 3 7
1 9 3 5 2 4
2 7 6 8 8 1
Что такое SettingWithCopyWarning
?
Чтобы знать, как бороться с этим предупреждением, важно понимать, что оно означает и почему оно поднимается в первую очередь.
При фильтрации DataFrames можно разрезать/индексировать кадр, чтобы вернуть либо представление, либо копию, в зависимости от внутренней компоновки и различных деталей реализации. "Представление" - это, как предполагает термин, представление исходных данных, поэтому изменение представления может изменить исходный объект. С другой стороны, "копия" - это репликация данных из оригинала, и изменение копии не влияет на оригинал.
Как уже упоминалось в других ответах, SettingWithCopyWarning
был создан для SettingWithCopyWarning
" SettingWithCopyWarning
назначения". Рассмотрите df
в настройке выше. Предположим, вы хотите выбрать все значения в столбце "B", где значения в столбце "A"> 5. Pandas позволяет вам делать это разными способами, некоторые из которых более правильные, чем другие. Например,
df[df.A > 5]['B']
1 3
2 6
Name: B, dtype: int64
А также,
df.loc[df.A > 5, 'B']
1 3
2 6
Name: B, dtype: int64
Они возвращают один и тот же результат, поэтому, если вы только читаете эти значения, это не имеет значения. Итак, в чем проблема? Проблема с цепным присваиванием заключается в том, что обычно сложно предсказать, будет ли возвращено представление или копия, поэтому это в значительной степени становится проблемой, когда вы пытаетесь присвоить значения обратно. Чтобы построить на предыдущем примере, рассмотрим, как этот код выполняется интерпретатором:
df.loc[df.A > 5, 'B'] = 4
# becomes
df.__setitem__((df.A > 5, 'B'), 4)
С одним вызовом __setitem__
df
. OTOH, рассмотрите этот код:
df[df.A > 5]['B'] = 4
# becomes
df.__getitem__(df.A > 5).__setitem__('B", 4)
Теперь, в зависимости от того, вернул ли __getitem__
представление или копию, операция __setitem__
может не работать.
В общем, вы должны использовать loc
для назначения на основе iloc
и iloc
для целочисленных/позиционных назначений, поскольку спецификация гарантирует, что они всегда работают с оригиналом. Кроме того, для установки одной ячейки вы должны использовать at
и iat
.
Больше можно найти в документации.
Заметка
Все операции логического индексирования, выполняемые с помощью loc
также можно выполнять с помощью iloc
. Единственное отличие состоит в том, что iloc
ожидает либо целые числа/позиции для индекса, либо пустой массив логических значений, а также целочисленные/позиционные индексы для столбцов.
Например,
df.loc[df.A > 5, 'B'] = 4
Может быть написано НАС
df.iloc[(df.A > 5).values, 1] = 4
А также,
df.loc[1, 'A'] = 100
Может быть написано как
df.iloc[1, 0] = 100
И так далее.
Просто скажи мне, как подавить предупреждение!
Рассмотрим простую операцию над столбцом "А" в df
. Выбор "А" и деление на 2 вызовет предупреждение, но операция будет работать.
df2 = df[['A']]
df2['A'] /= 2
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/__main__.py:1: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
df2
A
0 2.5
1 4.5
2 3.5
Есть несколько способов напрямую отключить это предупреждение:
-
Сделайте deepcopy
df2 = df[['A']].copy(deep=True)
df2['A'] /= 2
-
Изменить pd.options.mode.chained_assignment
Может быть установлен на None
, "warn"
или "raise"
. "warn"
- это значение по умолчанию. None
не SettingWithCopyError
предупреждение полностью, а "raise"
вызовет SettingWithCopyError
, не позволяющую SettingWithCopyError
операцию.
pd.options.mode.chained_assignment = None
df2['A'] /= 2
@Peter Cotton в комментариях придумал хороший способ ненавязчивого изменения режима (измененного из этой сущности) с помощью диспетчера контекста, чтобы установить режим только так долго, как это требуется, и сбросить его обратно к исходное состояние, когда закончено.
class ChainedAssignent:
def __init__(self, chained=None):
acceptable = [None, 'warn', 'raise']
assert chained in acceptable, "chained must be in " + str(acceptable)
self.swcw = chained
def __enter__(self):
self.saved_swcw = pd.options.mode.chained_assignment
pd.options.mode.chained_assignment = self.swcw
return self
def __exit__(self, *args):
pd.options.mode.chained_assignment = self.saved_swcw
Использование заключается в следующем:
# some code here
with ChainedAssignent():
df2['A'] /= 2
# more code follows
Или, чтобы поднять исключение
with ChainedAssignent(chained='raise'):
df2['A'] /= 2
SettingWithCopyError:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
"Проблема XY": что я делаю не так?
Часто пользователи пытаются найти способы подавления этого исключения, не понимая, почему оно возникло в первую очередь. Это хороший пример проблемы XY, когда пользователи пытаются решить проблему "Y", которая на самом деле является признаком более глубокой проблемы "X". Вопросы будут подняты на основе общих проблем, которые встречаются с этим предупреждением, и затем будут представлены решения.
Вопрос 1
У меня есть датафрейм
df
A B C D E
0 5 0 3 3 7
1 9 3 5 2 4
2 7 6 8 8 1
Я хочу присвоить значения в столбце "A"> от 5 до 1000. Мой ожидаемый результат
A B C D E
0 5 0 3 3 7
1 1000 3 5 2 4
2 1000 6 8 8 1
Неправильный способ сделать это:
df.A[df.A > 5] = 1000 # works, because df.A returns a view
df[df.A > 5]['A'] = 1000 # does not work
df.loc[df.A 5]['A'] = 1000 # does not work
Правильный путь с помощью loc
:
df.loc[df.A > 5, 'A'] = 1000
Вопрос 2 1
Я пытаюсь установить значение в ячейке (1, 'D') на 12345. Мой ожидаемый вывод
A B C D E
0 5 0 3 3 7
1 9 3 5 12345 4
2 7 6 8 8 1
Я пробовал разные способы доступа к этой ячейке, такие как df['D'][1]
. Каков наилучший способ сделать это?
1. Этот вопрос конкретно не связан с предупреждением, но полезно понять, как правильно выполнить эту конкретную операцию, чтобы избежать ситуаций, когда предупреждение может потенциально возникнуть в будущем.
Вы можете использовать любой из следующих методов, чтобы сделать это.
df.loc[1, 'D'] = 12345
df.iloc[1, 3] = 12345
df.at[1, 'D'] = 12345
df.iat[1, 3] = 12345
Вопрос 3
Я пытаюсь установить значения на основе некоторых условий. У меня есть датафрейм
A B C D E
1 9 3 5 2 4
2 7 6 8 8 1
Я хотел бы присвоить значения в "D" для 123, чтобы "C" == 5. Я пытался
df2.loc[df2.C == 5, 'D'] = 123
Который кажется нормальным, но я все еще получаю SettingWithCopyWarning
! Как это исправить?
Это на самом деле, вероятно, из-за кода выше в вашем конвейере. Вы создали df2
из чего-то большего, как
df2 = df[df.A > 5]
? В этом случае логическое индексирование вернет представление, поэтому df2
будет ссылаться на оригинал. Что вам нужно сделать, это назначить df2
для копии:
df2 = df[df.A > 5].copy()
# Or,
# df2 = df.loc[df.A > 5, :]
Вопрос 4
Я пытаюсь удалить столбец "C" на месте из
A B C D E
1 9 3 5 2 4
2 7 6 8 8 1
Но используя
df2.drop('C', axis=1, inplace=True)
SettingWithCopyWarning
. Почему это происходит?
Это связано с тем, что df2
должен быть создан как представление какой-либо другой операции нарезки, такой как
df2 = df[df.A > 5]
Решение здесь состоит в том, чтобы либо сделать copy()
из df
, либо использовать loc
, как и раньше.
Ответ 4
Pandas DataFrame предупреждение о копировании
Когда вы идете и делаете что-то вроде этого:
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
pandas.ix
этом случае pandas.ix
возвращает новый отдельный фрейм данных.
Любые значения, которые вы решите изменить в этом кадре данных, не изменят исходный кадр данных.
Это то, что панды пытаются предупредить вас.
Почему .ix
плохая идея
Объект .ix
пытается сделать больше, чем одно, и для любого, кто читал что-либо о чистом коде, это сильный запах.
Учитывая этот кадр данных:
df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})
Два поведения:
dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2
Поведение dfcopy
: dfcopy
теперь является отдельным фреймом данных. Изменение не изменит df
df.ix[0, "a"] = 3
Поведение два: Это меняет исходный фрейм данных.
.loc
этого используйте .loc
Разработчики панд признали, что объект .ix
был довольно вонючим [спекулятивно] и таким образом создали два новых объекта, которые помогают в доступе и назначении данных. (Другое существо .iloc
)
.loc
быстрее, потому что он не пытается создать копию данных.
.loc
предназначен для изменения вашего существующего информационного кадра на месте, что более эффективно использует память.
.loc
предсказуемо, у него одно поведение.
Решение
В примере кода вы загружаете большой файл с большим количеством столбцов, а затем изменяете его, чтобы он стал меньше.
Функция pd.read_csv
может помочь вам в этом, а также значительно ускорить загрузку файла.
Так что вместо этого
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]]
Сделай это
columns = ['STK', 'TPrice', 'TPCLOSE', 'TOpen', 'THigh', 'TLow', 'TVol', 'TAmt', 'TDate', 'TTime']
df = pd.read_csv(StringIO(str_of_all), sep=',', usecols=[0,3,2,1,4,5,8,9,30,31])
df.columns = columns
Это будет только читать интересующие вас столбцы и правильно называть их. Нет необходимости использовать злой объект .ix
для .ix
магических вещей.
Ответ 5
Чтобы устранить все сомнения, моим решением было сделать глубокую копию среза вместо обычной копии.
Это может быть неприменимо в зависимости от вашего контекста (ограничения/размер памяти среза, потенциал для снижения производительности - особенно если копия происходит в цикле, как это было для меня и т.д.)
Чтобы быть ясным, вот предупреждение, которое я получил:
/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
Ура!
Ответ 6
Здесь я отвечу на вопрос напрямую. Как с этим бороться?
Сделайте .copy(deep=False)
после .copy(deep=False)
. Смотрите pandas.DataFrame.copy.
Подождите, не вернет ли кусок копию? В конце концов, это то, что пытается сказать предупреждающее сообщение? Прочитайте длинный ответ:
import pandas as pd
df = pd.DataFrame({'x':[1,2,3]})
Это дает предупреждение:
df0 = df[df.x>2]
df0['foo'] = 'bar'
Это не:
df1 = df[df.x>2].copy(deep=False)
df1['foo'] = 'bar'
И df0
и df1
являются объектами DataFrame
, но что-то в них отличается, что позволяет пандам распечатать предупреждение. Давай узнаем что это такое.
import inspect
slice= df[df.x>2]
slice_copy = df[df.x>2].copy(deep=False)
inspect.getmembers(slice)
inspect.getmembers(slice_copy)
Используя выбранный вами инструмент сравнения, вы увидите, что кроме пары адресов, единственное существенное различие заключается в следующем:
| | slice | slice_copy |
| _is_copy | weakref | None |
Метод, который решает, следует ли предупреждать, является DataFrame._check_setitem_copy
который проверяет _is_copy
. Итак, поехали. Сделайте copy
чтобы ваш DataFrame не был _is_copy
.
В предупреждении предлагается использовать .loc
, но если вы используете .loc
для фрейма, который _is_copy
, вы все равно получите то же предупреждение. Ложная? Да. Раздражает? Вы ставите. Полезно? Потенциально, когда используется цепное назначение. Но он не может правильно определить назначение цепи и распечатывает предупреждение без разбора.
Ответ 7
Если вы назначили срез переменной и хотите установить ее с помощью следующей переменной:
df2 = df[df['A'] > 2]
df2['B'] = value
И вы не хотите использовать решение Jeffs, потому что ваше вычисление состояния df2
длинное или по какой-либо другой причине, вы можете использовать следующее:
df.loc[df2.index.tolist(), 'B'] = value
df2.index.tolist()
возвращает индексы из всех записей в df2, которые затем будут использоваться для установки столбца B в исходном фрейме.
Ответ 8
Это должно работать:
quote_df.loc[:,'TVol'] = quote_df['TVol']/TVOL_SCALE
Ответ 9
Я думаю, вы могли бы избежать всей этой проблемы:
return (
pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
.rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
.ix[:,[0,3,2,1,4,5,8,9,30,31]]
.assign(
TClose=lambda df: df['TPrice'],
RT=lambda df: 100 * (df['TPrice']/quote_df['TPCLOSE'] - 1),
TVol=lambda df: df['TVol']/TVOL_SCALE,
TAmt=lambda df: df['TAmt']/TAMT_SCALE,
STK_ID=lambda df: df['STK'].str.slice(13,19),
STK_Name=lambda df: df['STK'].str.slice(21,30)#.decode('gb2312'),
TDate=lambda df: df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10]),
)
)
Использование Assign. Из документации: Назначьте новые столбцы в DataFrame, возвращая новый объект (копию) со всеми исходными столбцами в дополнение к новым.
См. Статью Тома Аугспургера о цепочке методов в пандах: https://tomaugspurger.github.io/method-chaining
Ответ 10
Для меня эта проблема возникла в следующем > упрощенном < пример. И я также смог его решить (надеюсь, с правильным решением):
старый код с предупреждением:
def update_old_dataframe(old_dataframe, new_dataframe):
for new_index, new_row in new_dataframe.iterrorws():
old_dataframe.loc[new_index] = update_row(old_dataframe.loc[new_index], new_row)
def update_row(old_row, new_row):
for field in [list_of_columns]:
# line with warning because of chain indexing old_dataframe[new_index][field]
old_row[field] = new_row[field]
return old_row
Это напечатало предупреждение для строки old_row[field] = new_row[field]
Поскольку строки в методе update_row на самом деле имеют тип Series
, я заменил строку:
old_row.at[field] = new_row.at[field]
то есть. метод для доступа/поиска для Series
. Несмотря на то, что обе работают очень хорошо, и результат такой же, таким образом мне не нужно отключать предупреждения (= хранить их для других проблем с индексацией цепочки где-то еще).
Надеюсь, это поможет кому-то.
Ответ 11
Вопрос/замечание для начинающих
Может быть, разъяснение для других начинающих, как я (я из R, который, кажется, работает несколько иначе под капотом). Следующий безвредный на вид и функциональный код продолжал выдавать предупреждение SettingWithCopy, и я не мог понять, почему. Я и прочитал, и понял, что было получено с помощью "цепной индексации", но мой код не содержит ничего:
def plot(pdb, df, title, **kw):
df['target'] = (df['ogg'] + df['ugg']) / 2
# ...
Но потом, слишком поздно, я посмотрел, где вызывается функция plot():
df = data[data['anz_emw'] > 0]
pixbuf = plot(pdb, df, title)
Таким образом, "df" - это не фрейм данных, а объект, который каким-то образом запоминает, что он был создан путем индексации фрейма данных (так что это представление?), Которое сделает строку в plot()
df['target'] = ...
эквивалентно
data[data['anz_emw'] > 0]['target'] = ...
которая является цепной индексацией. Я правильно понял?
Тем не мение,
def plot(pdb, df, title, **kw):
df.loc[:,'target'] = (df['ogg'] + df['ugg']) / 2
починил это.
Ответ 12
Некоторые могут хотеть просто подавить предупреждение:
class SupressSettingWithCopyWarning:
def __enter__(self):
pd.options.mode.chained_assignment = None
def __exit__(self, *args):
pd.options.mode.chained_assignment = 'warn'
with SupressSettingWithCopyWarning():
#code that produces warning
Ответ 13
Эта тема действительно сбивает с толку Панд. К счастью, у него есть относительно простое решение.
Проблема в том, что не всегда понятно, возвращают ли операции фильтрации данных (например, loc) копию или представление DataFrame. Дальнейшее использование такого отфильтрованного DataFrame может, таким образом, привести к путанице
Простое решение (если вам не нужно работать с очень большими наборами данных):
Всякий раз, когда вам нужно обновить какие-либо значения, всегда убедитесь, что вы неявно копируете DataFrame перед назначением.
df # Some DataFrame
df = df.loc[:, 0:2] # Some filtering (unsure whether a view or copy is returned)
df = df.copy() # Ensuring a copy is made
df[df["Name"] == "John"] = "Johny" # Assignment can be done now (no warning)