Образец кода
# My code
df.loc[0, 'column_name'] = 'foo bar'
Этот код в Pandas 20.3 выдает SettingWithCopyWarning и предлагает
«Попробуйте вместо этого использовать .loc[row_indexer,col_indexer] = value
».
Я уже так делаю, похоже есть небольшая ошибка. Я использую Jupyter.
Спасибо! :)
pd.show_versions()
коммит: Нет
питон: 3.6.1.final.0
биты Python: 64
ОС: Windows
ОС-релиз: 8.1
машина: AMD64
процессор: Intel64 Family 6 Model 61 Stepping 4, GenuineIntel
byteorder: маленький
LC_ALL: Нет
ЯЗЫК: Нет
МЕСТО: Нет.
панды: 0.20.1
pytest: 3.0.7
пункт: 9.0.1
setuptools: 35.0.2
Cython: 0,25,2
число: 1.12.1
scipy: 0.19.0
xarray: Нет
IPython: 5.3.0
сфинкс: 1.5.6
Пэтси: 0.4.1
dateutil: 2.6.0
pytz: 2017.2
blosc: Нет
узкое место: 1.2.1
таблицы: 3.2.2
numexpr: 2.6.2
перо: Нет
matplotlib: 2.0.2
openpyxl: Нет
xlrd: 1.0.0
xlwt: 1.2.0
xlsxwriter: 0.9.6
лхмл: 3.7.3
BS4: 4.6.0
html5lib: 0,999
sqlalchemy: 1.1.9
pymysql: Нет
psycopg2: Нет
jinja2: 2.9.6
s3fs: Нет
pandas_gbq: Нет
pandas_datareader: Нет
@NadiaRom Можете привести полный пример? Трудно сказать наверняка, но я подозреваю, что df
возникло в результате операции, которая может быть просмотром или копированием. Например:
In [8]: df = pd.DataFrame({"A": [1, 2], "B": [3, 4], "C": [4, 5]})
In [9]: df1 = df[['A', 'B']]
In [10]: df1.loc[0, 'A'] = 5
/Users/taugspurger/Envs/pandas-dev/lib/python3.6/site-packages/pandas/pandas/core/indexing.py:180: 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
self._setitem_with_indexer(indexer, value)
/Users/taugspurger/Envs/pandas-dev/bin/ipython:1: 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
#!/Users/taugspurger/Envs/pandas-dev/bin/python3.6
Итак, мы правильно обновляем df1
. Неясно, будет ли также обновляться df
. Я думаю, что то же самое происходит и с вами, но без воспроизводимого примера трудно сказать наверняка.
@TomAugspurger Вот код, в общем, я никогда не присваиваю значения пандам без .loc
df = pd.read_csv('df_unicities.tsv', sep='\t')
df.replace({'|': '--'}, inplace=True)
df_c = df.loc[df.encountry == country, : ]
df_c['sort'] = (df_c.encities_ua == 'all').astype(int) # new column
df_c['sort'] += (df_c.encities_foreign == 'all').astype(int)
df_c.sort_values(by='sort', inplace=True)
# ---end of chunk, everything is fine ---
if df_c.encities_foreign.str.contains('all').sum() < len(df_c):
df_c.loc[df_c.encities_foreign.str.contains('all'), 'encities_foreign'] = 'other'
df_c.loc[df_c.cities_foreign.str.contains('всі'), 'cities_foreign'] = 'інші'
else:
df_c.loc[df_c.encities_foreign.str.contains('all'), 'encities_foreign'] = country
df_c.loc[df_c.cities_foreign.str.contains('всі'), 'cities_foreign'] = df_c.country.iloc[0]
if df_c.encities_ua.str.contains('all').sum() < len(df_c):
df_c.loc[df_c.encities_ua.str.contains('all'), 'encities_ua'] = 'other'
df_c.loc[df_c.cities_ua.str.contains('всі'), 'cities_ua'] = 'інші'
else:
df_c.loc[df_c.encities_ua.str.contains('all'), 'encities_ua'] = 'Ukraine'
df_c.loc[df_c.cities_ua.str.contains('всі'), 'cities_ua'] = 'Україна'
# Warning after it
Спасибо за быстрый ответ!
Проблема здесь в том, что вы сначала нарезаете свой фрейм данных с помощью .loc
в строке 4. Попытка присвоить значения этому фрагменту.
df_c = df.loc[df.encountry == country, :]
Pandas не уверен на 100%, хотите ли вы присвоить значения только своему df_c
срезу или полностью распространить его до исходного df
. Чтобы избежать этого, когда вы впервые назначаете df_c
убедитесь, что вы сообщите pandas, что это его собственный фрейм данных (а не срез), используя
df_c = df.loc[df.encountry == country, :].copy()
Это исправит вашу ошибку. Я приведу краткий пример, чтобы объяснить вышесказанное, поскольку я заметил, что многие пользователи путаются с пандами в этом аспекте.
>>> import pandas as pd
>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df
A B
0 1 Q
1 2 Q
2 3 Q
3 4 C
4 5 C
>>> df.loc[df['B'] == 'Q', 'new_col'] = 'hello'
>>> df
A B new_col
0 1 Q hello
1 2 Q hello
2 3 Q hello
3 4 C NaN
4 5 C NaN
Итак, все вышесказанное работает так, как мы и ожидали! Теперь давайте попробуем пример, который отражает то, что вы пытались сделать со своими данными.
>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df_q = df.loc[df['B'] == 'Q']
>>> df_q
A B
0 1 Q
1 2 Q
2 3 Q
>>> df_q.loc[df['A'] < 3, 'new_col'] = 'hello'
/Users/riddellcd/anaconda/lib/python3.6/site-packages/pandas/core/indexing.py:337: 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
See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
self.obj[key] = _infer_fill_value(value)
>>> df_q
A B new_col
0 1 Q hello
1 2 Q hello
2 3 Q NaN
Похоже, мы получили ту же ошибку! Но это изменило df_q
как мы и ожидали! Это потому, что df_q
- это часть df
поэтому, даже если мы используем .loc [] df_q
pandas предупреждает нас, что он не будет распространять изменения вверх в df
. Чтобы избежать этого, нам нужно быть более явным и сказать, что df_q
- это собственный фрейм данных, отдельный от df
, явным образом объявив это так.
Давайте начнем с df_q
но .copy()
этот раз используем
>>> df_q = df.loc[df['B'] == 'Q'].copy()
>>> df_q
A B
0 1 Q
1 2 Q
2 3 Q
Lets try to reassign our value now!
>>> df_q.loc[df['A'] < 3, 'new_col'] = 'hello'
>>> df_q
A B new_col
0 1 Q hello
1 2 Q hello
2 3 Q NaN
Это работает без ошибок, потому что мы сказали пандам, что df_q
отделен от df
Если вы действительно хотите, чтобы эти изменения в df_c
распространялись до df
другой пункт и вы ответите, если хотите.
@CRiddler Отлично, спасибо !
Как вы упомянули, цепочка .loc
никогда не возвращала неожиданных результатов. Насколько я понимаю, .copy()
гарантирует Pandas, что мы рассматриваем выбранный df_sliced_once
как отдельный объект и не собираемся изменять исходный полный df
. Пожалуйста, поправьте, если я что-л. Перепутала.
документация находится здесь http://pandas.pydata.org/pandas-docs/stable/indexing.html#returning -a-view-versus-a-copy, а у @CRiddler есть хорошее объяснение. вы вообще НЕ должны использовать inplace
.
Если вы действительно хотите, чтобы эти изменения в
df_c
распространялись доdf
другая точка, и вы ответите, если хотите.
@CRiddler Спасибо, ваш ответ лучше, чем ответы в Stack Overflow, не могли бы вы добавить, если хотите перейти к исходному фрейму данных или указать, как это делается?
@persep В целом мне не нравится превращать проблемы в потоки stackoverflow для помощи, но кажется, что эта проблема привлекла достаточно внимания с момента последней публикации, поэтому я продолжу и опубликую свой метод решения этой проблемы в панды. Я обычно делаю это, не разделяя фрейм данных на отдельные переменные, а вместо этого превращаю маски в переменные, а затем комбинирую маски по мере необходимости и устанавливаю значения на основе этих масок, чтобы гарантировать, что изменения происходят в исходном фрейме данных, а не в некоторой копии, плавающей вокруг .
Исходные данные:
>>>import pandas as pd
>>> df = pd.DataFrame({'A':[1,2,3,4,5], 'B':list('QQQCC')})
>>> df
A B
0 1 Q
1 2 Q
2 3 Q
3 4 C
4 5 C
Помните, что создание временного фрейма данных НЕ приведет к распространению изменений
Как показано в предыдущем примере, это вносит изменения только в df_q
и вызывает предупреждение pandas (здесь не копируется / вставляется). И НЕ распространяет никаких изменений на df
>>> df_q = df.loc[df["B"] == "Q"]
>>> df_q.loc[df["A"] < 3, "new_column"] = "hello"
# df remains unchanged because we only made changes to `df_q`
>>> df
A B
0 1 Q
1 2 Q
2 3 Q
3 4 C
4 5 C
Насколько мне известно, нет способа использовать тот же код, что и выше, и принудительно распространить изменения обратно на исходный фрейм данных.
Однако, если мы немного изменим наше мышление и будем работать с масками вместо полноценных подмножеств, мы сможем достичь желаемого результата. Хотя это не обязательно «распространение» изменений в исходный фрейм данных из подмножества, мы гарантируем, что любые изменения, которые мы действительно вносим, произойдут в исходном фрейме данных df
. Для этого мы сначала создаем маски, а затем применяем их, когда хотим внести изменения в это подмножество df
>>> q_mask = df["B"] == "Q"
>>> a_mask = df["A"] < 3
# Combine masks (in this case we used "&") to achieve what a nested subset would look like
# In the same step we add in our item assignment. Instructing pandas to create a new column in `df` and assign
# the value "hello" to the rows in `df` where `q_mask` & `a_mask` overlap.
>>> df.loc[q_mask & a_mask, "new_col"] = "hello"
# Successful "propagation" of new values to the original dataframe
>>> df
A B new_col
0 1 Q hello
1 2 Q hello
2 3 Q NaN
3 4 C NaN
4 5 C NaN
Наконец, если мы когда-нибудь захотим увидеть, как будет выглядеть df_q, мы всегда можем подмножество его из исходного фрейма данных, используя наш q_mask
>>> df.loc[q_mask, :]
A B new_col
0 1 Q hello
1 2 Q hello
2 3 Q NaN
Хотя это не обязательно "распространение" изменений с df_q
на df
мы достигаем того же результата. Фактическое распространение должно быть выполнено явно и будет менее эффективным, чем простая работа с масками.
@CRiddler Спасибо, вы очень помогли
Самый полезный комментарий
Проблема здесь в том, что вы сначала нарезаете свой фрейм данных с помощью
.loc
в строке 4. Попытка присвоить значения этому фрагменту.Pandas не уверен на 100%, хотите ли вы присвоить значения только своему
df_c
срезу или полностью распространить его до исходногоdf
. Чтобы избежать этого, когда вы впервые назначаетеdf_c
убедитесь, что вы сообщите pandas, что это его собственный фрейм данных (а не срез), используяЭто исправит вашу ошибку. Я приведу краткий пример, чтобы объяснить вышесказанное, поскольку я заметил, что многие пользователи путаются с пандами в этом аспекте.
Пример с выдуманными данными
Итак, все вышесказанное работает так, как мы и ожидали! Теперь давайте попробуем пример, который отражает то, что вы пытались сделать со своими данными.
Похоже, мы получили ту же ошибку! Но это изменило
df_q
как мы и ожидали! Это потому, чтоdf_q
- это частьdf
поэтому, даже если мы используем .loc []df_q
pandas предупреждает нас, что он не будет распространять изменения вверх вdf
. Чтобы избежать этого, нам нужно быть более явным и сказать, чтоdf_q
- это собственный фрейм данных, отдельный отdf
, явным образом объявив это так.Давайте начнем с
df_q
но.copy()
этот раз используемЭто работает без ошибок, потому что мы сказали пандам, что
df_q
отделен отdf
Если вы действительно хотите, чтобы эти изменения в
df_c
распространялись доdf
другой пункт и вы ответите, если хотите.