избавиться от предупреждения pandas copy local [duplicate]

== тесты для ссылочного равенства (независимо от того, являются ли они одним и тем же объектом).

.equals() тесты для равенства значений (независимо от того, являются ли они логически «равными»).

Objects.equals () проверяет наличие null перед вызовом .equals(), поэтому вам не нужно (доступно с JDK7, также доступным в Guava ).

String.contentEquals () сравнивает содержимое String с содержимым любого CharSequence (доступно с Java 1.5).

Следовательно, если вы хотите проверить, имеет ли две строки одно и то же значение, вы, вероятно, захотите использовать Objects.equals().

// These two have the same value
new String("test").equals("test") // --> true 

// ... but they are not the same object
new String("test") == "test" // --> false 

// ... neither are these
new String("test") == new String("test") // --> false 

// ... but these are because literals are interned by 
// the compiler and thus refer to the same object
"test" == "test" // --> true 

// ... string literals are concatenated by the compiler
// and the results are interned.
"test" == "te" + "st" // --> true

// ... but you should really just call Objects.equals()
Objects.equals("test", new String("test")) // --> true
Objects.equals(null, "test") // --> false
Objects.equals(null, null) // --> true

Вы почти всегда хотите использовать Objects.equals(). В редкой ситуации, когда вы знаете, что имеете дело с интернированными строками, вы можете использовать ==.

Из JLS 3.10. 5. Строковые литералы :

Кроме того, строковый литерал всегда ссылается на тот же экземпляр класса String. Это связано с тем, что строковые литералы, или, в более общем смысле, строки, которые являются значениями константных выражений ( §15.28 ), «интернированы», чтобы обмениваться уникальными экземплярами, используя метод String.intern.

. Подобные примеры также можно найти в JLS 3.10.5-1 .

312
задан Brad Solomon 1 December 2017 в 17:15
поделиться

7 ответов

В целом точка SettingWithCopyWarning должна показывать пользователям (и другим пользователям), что они могут работать на копии, а не на оригинале, как они думают. Там есть ложные срабатывания (IOW вы знаете, что делаете, поэтому ok ). Одна из возможностей - просто отключить (по умолчанию предупреждение warn ), как предлагает @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 показывает выше, в то время как законный, и, вероятно, что-то, что я делаю, является технически обоснованным для этого предупреждения , а не ложным положительным. Другой способ not иметь предупреждение - выполнить операцию выбора через reindex, например

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

Или,

quote_df = quote_df.reindex(['STK',.......], axis=1) # v.0.21
102
ответ дан coldspeed 16 August 2018 в 11:52
поделиться
  • 1
    Я думаю, что в основном я бы предпочел не предупредить об этом вообще. Если вы работаете с синтаксисом с привязкой, вы можете определенно определить порядок индексирования, который должен произойти, чтобы он работал как ожидалось в любой заданной ситуации. Я думаю, что чересчур параноидально, что есть эти исчерпывающие меры предосторожности. В том же духе, что и «чтобы все были взрослыми». о методах или атрибутах «частного» класса, я считаю, что для панд лучше позволить пользователям быть взрослыми о прикомандированных назначениях. Используйте их только в том случае, если вы знаете, что делаете. – ely 17 December 2013 в 22:58
  • 2
    Немного unPythonic, чтобы попытаться предупредить людей, когда они начинают взломать альтернативы. Методы Pandas нового типа для доступа (улучшенные .ix, улучшенные .iloc и т. Д.) Могут определенно рассматриваться как «основной способ». без предупреждения, непрестанно о других путях. Вместо этого пусть они будут взрослыми, и если они захотят сделать прикованное назначение, пусть будет так. Мои два цента в любом случае. Здесь часто встречаются недовольные комментарии от разработчиков Pandas, когда прикомандированные назначения будут работать для решения проблемы, но не будут рассматриваться как «первичные», способ сделать это. – ely 17 December 2013 в 23:00
  • 3
    Спасибо за информацию & amp; обсуждение, я просто выключаю предупреждение, чтобы консоль молчала об этом. Это звучит как вид & amp; таблицу в базе данных SQL. Мне нужно знать больше о преимуществах внедрения концепции «копия», но ИМХО это немного бремя, чтобы позаботиться о тонкой семантической синтаксической разнице. – bigbug 18 December 2013 в 06:38
  • 4
    @EMS проблема в том, что не всегда ясно из кода , где делается копия или представление, и из этой проблемы возникает ряд ошибок / путаницы. Мы рассматривали возможность вставки файла / параметров rc для автоматической настройки, что может быть более полезным, учитывая, как работает настройка с предупреждением о копировании. – Jeff Tratner 19 December 2013 в 00:14
  • 5
    Является ли это понятным из кода или нет, похоже на мнение меня. Работа в достаточном количестве случаев делает его столь же понятным, как и любой другой сложный сторонний API. Я не понимаю, в чем дело. Если это исправлено с предупреждениями, то наверняка тонны и тонны других вещей тоже должны быть, и вы получите предупреждения для всех. – ely 19 December 2013 в 00:18
  • 6
    Я согласен с копией (); это ясно, и он исправил мою проблему (что было ложным положительным). – rdchambers 25 April 2015 в 14:27
  • 7
    после обновления до 0.16 я вижу гораздо больше ложных срабатываний, проблема с ложными срабатываниями - это то, что вы учитесь игнорировать его, хотя иногда это и является законным. – dashesy 6 July 2015 в 21:58
  • 8
    @dashy, вам не хватает точки. иногда может быть даже наиболее того времени, когда он может работать. Но это может произойти, например, если кадр больше / меньше или вы добавляете столбец, говорящий о другом dtype, что не работает . В этом-то и дело. Вы делаете что-то, что может работать, но не гарантируется. Это очень отличается от предупреждений об устаревании. Если вы хотите продолжить использовать его, и он работает, отлично. Но будьте предупреждены. – Jeff 7 July 2015 в 16:09
  • 9
    @Jeff имеет смысл сейчас, поэтому это поведение undefined. Если что-нибудь, то он должен выкинуть ошибку (во избежание ошибок, увиденных в C), так как api заморожен, то текущее поведение предупреждения имеет смысл для обратной совместимости. И я сделаю их бросить, чтобы поймать их как ошибки в моем производственном коде (warnings.filterwarnings('error', r'SettingWithCopyWarning). Также предложение использовать .loc иногда также не помогает (если оно находится в группе). – dashesy 7 July 2015 в 18:50
  • 10
    Причина для предупреждения - это, конечно, для людей, обновляющих старый код. И мне определенно нужно предупреждение, потому что я имею дело с очень уродливым старым кодом. – Thomas Andrews 1 November 2016 в 19:34
  • 11
    – Lachlan.00 13 July 2018 в 01:08

Предупреждение о резервном копировании данных Pandas

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

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

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 теперь является автономным фреймворком данных. Изменение этого параметра не изменится df

df.ix[0, "a"] = 3

Поведение два: это изменяет исходный фрейм.

Используйте .loc вместо

Разработчики pandas признали что объект .ix был довольно вонючим [спекулятивно] и таким образом создал два новых объекта, которые помогают при вступлении и назначении данных. (Другое значение .iloc)

.loc выполняется быстрее, потому что он не пытается создать копию данных.

.loc предназначен для изменения существующего (f17)

.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 для создания магического материала.

31
ответ дан firelynx 16 August 2018 в 11:52
поделиться

Вы можете избежать всей этой проблемы, я полагаю:

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

2
ответ дан Halee 16 August 2018 в 11:52
поделиться

Это должно работать:

quote_df.loc[:,'TVol'] = quote_df['TVol']/TVOL_SCALE
0
ответ дан jrouquie 16 August 2018 в 11:52
поделиться

Для меня эта проблема возникла в следующем упрощенном & lt; пример. И я также смог его решить (надеюсь, с правильным решением):

старый код с предупреждением:

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. Несмотря на то, что оба работают очень хорошо, и результат тот же, таким образом, мне не нужно отключать предупреждения (= держать их для других проблем с индексацией цепочки где-то еще).

Надеюсь, это может кому-то помочь.

0
ответ дан Petr Szturc 16 August 2018 в 11:52
поделиться

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

To ясно, вот предупреждение, которое я получил:

/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: удаление столбца оригинала влияет на копию

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

>> 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

Приветствия!

5
ответ дан Raphvanns 16 August 2018 в 11:52
поделиться

Если вы назначили срез переменной и хотите установить эту переменную следующим образом:

df2 = df[df['A'] > 2]
df2['B'] = value

И вы не хотите использовать решение Jeffs, потому что ваше вычисление состояния df2 длится или по какой-либо другой причине, вы можете использовать следующее:

df.loc[df2.index.tolist(), 'B'] = value

df2.index.tolist() возвращает индексы из всех записей в df2, которые затем будут использоваться для установки столбца B в оригинале dataframe.

1
ответ дан Steohan 16 August 2018 в 11:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: