Фон
Я только что обновил свои панды с 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])
df.set_value
документацию здесь - pandas.pydata.org/pandas-docs/stable/generated/…df.set_value
устарела. Панды теперь рекомендует использовать.at[]
или.iat[]
вместо. документы здесь pandas.pydata.org/pandas-docs/stable/generated/…option_context
здесь панд : pandas.pydata.org/pandas-docs/stable/user_guide/options.html , используйте какwith pd.option_context("mode.chained_assignment", None): [...]
Ответы:
SettingWithCopyWarning
Был создан , чтобы флаг потенциально запутанным «прикован» задания, такие как следующий, который не всегда работает , как и следовало ожидать, в частности , когда первый выбор возвращает копию . [см. GH5390 и GH5597 для справочного обсуждения.]Предупреждение предлагает переписать следующее:
Тем не менее, это не подходит для вашего использования, что эквивалентно:
Хотя очевидно, что вам не нужны записи, возвращающие его в исходный фрейм (поскольку вы перезаписываете ссылку на него), к сожалению, этот шаблон нельзя отличить от первого примера связанного присваивания. Отсюда (ложное срабатывание) предупреждение. Возможность ложных срабатываний рассматривается в документах по индексации , если вы хотите прочитать дальше. Вы можете безопасно отключить это новое предупреждение с помощью следующего назначения.
источник
.ix
, улучшенные.iloc
и т. Д.) Могут определенно рассматриваться как «основной путь», не предупреждая всех непрерывно о других способах. Вместо этого позвольте им быть взрослыми, и если они хотят выполнять связанное назначение, пусть будет так. Мои два цента в любом случае. Здесь часто встречаются недовольные комментарии разработчиков Pandas, когда цепные задания будут работать для решения проблемы, но не будут рассматриваться как «основной» способ сделать это.pd.options.mode.chained_assignment = None
мой код работает примерно в 6 раз быстрее. Кто-нибудь еще испытывал подобные результаты?Этот пост предназначен для читателей, которые,
Настроить
Что это
SettingWithCopyWarning
?Чтобы знать, как бороться с этим предупреждением, важно понимать, что оно означает и почему оно поднимается в первую очередь.
При фильтрации DataFrames можно разрезать / индексировать кадр, чтобы вернуть либо представление , либо копию , в зависимости от внутренней компоновки и различных деталей реализации. «Представление» - это, как предполагает термин, представление исходных данных, поэтому изменение представления может изменить исходный объект. С другой стороны, «копия» - это репликация данных из оригинала, и изменение копии не влияет на оригинал.
Как уже упоминалось в других ответах, он
SettingWithCopyWarning
был создан для пометки операций «цепного присваивания». Рассмотримdf
в настройках выше. Предположим, вы хотите выбрать все значения в столбце «B», где значения в столбце «A»> 5. Pandas позволяет вам делать это разными способами, некоторые из которых более правильные, чем другие. Например,А также,
Они возвращают один и тот же результат, поэтому, если вы только читаете эти значения, это не имеет значения. Итак, в чем проблема? Проблема с цепным присваиванием заключается в том, что обычно сложно предсказать, будет ли возвращено представление или копия, поэтому это в значительной степени становится проблемой, когда вы пытаетесь присвоить значения обратно. Чтобы построить на предыдущем примере, рассмотрим, как этот код выполняется интерпретатором:
С одного
__setitem__
звонкаdf
. OTOH, рассмотрите этот код:Теперь, в зависимости от того,
__getitem__
возвращены ли представление или копия,__setitem__
операция может не работать .В общем, вы должны использовать
loc
для назначения на основе меток иiloc
для целочисленного / позиционного назначения, поскольку спецификация гарантирует, что они всегда работают с оригиналом. Кроме того, для настройки отдельной ячейки вы должны использоватьat
иiat
.Больше можно найти в документации .
Просто скажи мне, как подавить предупреждение!
Рассмотрим простую операцию над столбцом «А»
df
. Выбор «А» и деление на 2 вызовет предупреждение, но операция будет работать.Есть несколько способов напрямую отключить это предупреждение:
Сделать
deepcopy
Изменение
pd.options.mode.chained_assignment
Может быть установлено
None
,"warn"
или"raise"
."warn"
по умолчанию.None
полностью отключит предупреждение и"raise"
выдаст сообщениеSettingWithCopyError
, не позволяющее завершить операцию.@Peter Cotton в комментариях придумал хороший способ ненавязчивого изменения режима (измененного из этой сущности ) с помощью диспетчера контекста, чтобы установить режим только так долго, как это требуется, и сбросить его обратно к исходное состояние, когда закончено.
Использование заключается в следующем:
Или, чтобы поднять исключение
«Проблема XY»: что я делаю не так?
Часто пользователи пытаются искать способы подавления этого исключения, не понимая, почему оно возникло в первую очередь. Это хороший пример проблемы XY , когда пользователи пытаются решить проблему «Y», которая на самом деле является признаком более глубокой проблемы «X». Вопросы будут подняты на основе общих проблем, которые встречаются с этим предупреждением, и затем будут представлены решения.
Неправильный способ сделать это:
Правильный способ использования
loc
:Вы можете использовать любой из следующих методов, чтобы сделать это.
Это на самом деле, вероятно, из-за кода выше в вашем конвейере. Вы создали
df2
из чего-то большего, как? В этом случае логическое индексирование вернет представление, поэтому
df2
будет ссылаться на оригинал. Что вам нужно сделать , это назначитьdf2
на копию :Это потому, что,
df2
должно быть, было создано как представление от какой-либо другой операции нарезки, такой какРешение здесь либо сделать
copy()
изdf
, или использованияloc
, как и раньше.источник
В целом, цель
SettingWithCopyWarning
состоит в том, чтобы показать пользователям (и особенно новым пользователям), что они могут работать с копией, а не с оригиналом, как они думают. Там являются ложными срабатывания (IOW , если вы знаете , что вы делаете , это может быть хорошо ). Одна из возможностей - просто отключить (по умолчанию предупреждение ) предупреждение, как предлагает @Garrett.Вот еще один вариант:
Вы можете установить
is_copy
флагFalse
, который будет эффективно отключать проверку для этого объекта :Если вы явно копируете, больше не будет предупреждений:
Код, который OP показывает выше, хотя он и является законным, и, вероятно, что-то, что я делаю, является технически обоснованным для этого предупреждения, а не ложным срабатыванием. Другой способ не иметь предупреждения - выполнить операцию выбора с помощью
reindex
, например,Или,
источник
0.16
я вижу еще много ложных срабатываний, проблема со ложными срабатываниями - это научиться игнорировать это, хотя иногда это и законно.undefined
поведение. Во всяком случае, это должно привести к ошибке (чтобы избежать ошибок, замеченных вC
), посколькуapi
текущее поведение предупреждения имеет смысл для обратной совместимости. И я заставлю их бросать, чтобы ловить их как ошибки в моем производственном коде (warnings.filterwarnings('error', r'SettingWithCopyWarning
). Кроме того, предложение использовать.loc
иногда также не помогает (если оно в группе).Pandas DataFrame предупреждение о копировании
Когда вы идете и делаете что-то вроде этого:
pandas.ix
в этом случае возвращает новый отдельный фрейм данных.Любые значения, которые вы решите изменить в этом кадре данных, не изменят исходный кадр данных.
Это то, что панды пытаются предупредить вас.
Почему
.ix
плохая идея.ix
Объект пытается сделать больше , чем одну вещь, и для тех , кто читал ничего о чистом коде, это сильный запах.С учетом этого кадра данных:
Два поведения:
Поведение первое:
dfcopy
теперь это отдельный массив данных. Изменение не изменитсяdf
Поведение два: Это меняет исходный фрейм данных.
Используйте
.loc
вместоРазработчики панд признали, что
.ix
объект был довольно вонючим [спекулятивно] и таким образом создали два новых объекта, которые помогают в доступе и назначении данных. (Другое существо.iloc
).loc
быстрее, потому что он не пытается создать копию данных..loc
предназначен для изменения вашего существующего информационного кадра на месте, что более эффективно использует память..loc
предсказуемо, у него одно поведение.Решение
В примере кода вы загружаете большой файл с большим количеством столбцов, а затем модифицируете его, чтобы он уменьшился.
pd.read_csv
Функция может помочь вам с большим количеством этого , а также сделать загрузку файла намного быстрее.Так что вместо этого
Сделай это
Это будет читать только интересующие вас столбцы и правильно называть их. Нет необходимости использовать злой
.ix
объект, чтобы делать магические вещи.источник
.iloc
. Это два основных метода индексации структур данных панд. Подробнее читайте в документации.Здесь я отвечаю на вопрос напрямую. Как с этим бороться?
Сделайте
.copy(deep=False)
после того, как вы нарежете. Смотрите pandas.DataFrame.copy .Подождите, не вернет ли кусок копию? В конце концов, это то, что пытается сказать предупреждающее сообщение? Прочитайте длинный ответ:
Это дает предупреждение:
Это не:
Оба
df0
иdf1
являютсяDataFrame
объектами, но что-то в них отличается, что позволяет пандам распечатать предупреждение. Давайте выясним, что это.Используя выбранный вами diff-инструмент, вы увидите, что кроме пары адресов, единственное существенное различие заключается в следующем:
Метод, который решает, предупреждать ли, - это
DataFrame._check_setitem_copy
какие проверки_is_copy
. Итак, поехали. Сделайтеcopy
так, чтобы ваш DataFrame не был_is_copy
.Предупреждение предлагает использовать
.loc
, но если вы используете.loc
на кадре_is_copy
, вы все равно получите то же предупреждение. Вводя в заблуждение? Да. Раздражает? Вы ставите. Полезно? Потенциально, когда используется цепное назначение. Но он не может правильно определить назначение цепи и распечатывает предупреждение без разбора.источник
Эта тема действительно путает с пандами. К счастью, у него есть относительно простое решение.
Проблема в том, что не всегда понятно, возвращают ли операции фильтрации данных (например, loc) копию или представление DataFrame. Дальнейшее использование такого отфильтрованного DataFrame может, следовательно, ввести в заблуждение.
Простое решение (если вам не нужно работать с очень большими наборами данных):
Всякий раз, когда вам нужно обновить какие-либо значения, всегда убедитесь, что вы неявно копируете DataFrame перед назначением.
источник
Чтобы устранить любые сомнения, я решил сделать глубокую копию среза вместо обычной копии. Это может быть неприменимо в зависимости от вашего контекста (ограничения памяти / размер фрагмента, возможность снижения производительности - особенно, если копирование происходит в цикле, как это было для меня, и т. Д ...)
Чтобы быть ясным, вот предупреждение, которое я получил:
иллюстрация
У меня были сомнения, что предупреждение было выдано из-за столбца, который я помещал на копию фрагмента. Технически не пытаясь установить значение в копии среза, это все же была модификация копии среза. Ниже приведены (упрощенные) шаги, которые я предпринял для подтверждения подозрения. Я надеюсь, что это поможет тем из нас, кто пытается понять предупреждение.
Пример 1: удаление столбца на оригинале влияет на копию
Мы уже знали это, но это здоровое напоминание. Это НЕ то, о чем идет речь.
Можно избежать изменений, внесенных в df1, чтобы повлиять на df2
Пример 2: удаление столбца на копии может повлиять на оригинал
Это на самом деле иллюстрирует предупреждение.
Можно избежать изменений, внесенных в df2, чтобы повлиять на df1
Ура!
источник
Это должно работать:
источник
Некоторые могут хотеть просто подавить предупреждение:
источник
Если вы присвоили срез переменной, и хотите установить ее, как показано ниже:
И вы не хотите использовать решение Jeffs, потому что ваши условия вычислений слишком
df2
длинные или по какой-то другой причине, тогда вы можете использовать следующее:df2.index.tolist()
возвращает индексы из всех записей в df2, которые затем будут использоваться для установки столбца B в исходном кадре данных.источник
Для меня эта проблема произошла в следующем> упрощенном <примере. И я также смог решить ее (надеюсь, с правильным решением):
старый код с предупреждением:
Это напечатало предупреждение для строки
old_row[field] = new_row[field]
Поскольку строки в методе update_row на самом деле являются типами
Series
, я заменил строку на:т.е. метод доступа / поиска для
Series
. Несмотря на то, что оба работают просто отлично и результат один и тот же, таким образом мне не нужно отключать предупреждения (= оставляйте их для других проблем индексации цепочек где-то еще).Я надеюсь, что это может кому-то помочь.
источник
Вы можете избежать всей проблемы, как это, я считаю:
Используя Назначить. Из документации : Назначьте новые столбцы в DataFrame, возвращая новый объект (копию) со всеми исходными столбцами в дополнение к новым.
См. Статью Тома Аугспургера о цепочках методов в пандах: https://tomaugspurger.github.io/method-chaining.
источник
Вопрос / замечание для начинающих
Может быть, разъяснение для других начинающих, как я (я из R, который, кажется, работает несколько иначе под капотом). Следующий безвредный на вид и функциональный код продолжал выдавать предупреждение SettingWithCopy, и я не мог понять, почему. Я и прочитал, и понял, что было получено с помощью «цепной индексации», но мой код не содержит ничего:
Но потом, слишком поздно, я посмотрел, где вызывается функция plot ():
Таким образом, "df" - это не фрейм данных, а объект, который каким-то образом запоминает, что он был создан путем индексации фрейма данных (так что это представление?), Которое сделает линию в plot ()
эквивалентно
которая является цепной индексацией. Я правильно понял?
Тем не мение,
починил это.
источник
Поскольку этот вопрос уже полностью объяснен и обсужден в существующих ответах, я просто предоставлю аккуратный
pandas
подход к использованию менеджера контекстаpandas.option_context
(ссылки на документы и пример). ) - абсолютно не нужно создавать собственный класс со всеми методами dunder и другими функциями. и свистит.Сначала код самого менеджера контекста:
Тогда пример:
Стоит заметить, что оба подхода не изменяются
a
, что немного удивляет меня, и даже мелкая копия df.copy(deep=False)
помешает появлению этого предупреждения (насколько я понимаю, мелкая копия должна хотя бы изменитьa
, но это не так). т.pandas
волшебство.).источник
Я получил эту проблему
.apply()
при назначении нового кадра данных из ранее существующего кадра данных, на котором я использовал.query()
метод. Например:Вернул бы эту ошибку. Исправление, которое, кажется, устраняет ошибку в этом случае, изменяя это на:
Однако это НЕ эффективно, особенно при использовании больших фреймов данных, из-за необходимости делать новую копию.
Если вы используете
.apply()
метод при создании нового столбца и его значений, исправление, которое устраняет ошибку и является более эффективным, заключается в добавлении.reset_index(drop=True)
:источник