Amostra de Código
# My code
df.loc[0, 'column_name'] = 'foo bar'
Este código no Pandas 20.3 lança SettingWithCopyWarning e sugere
"Tente usar .loc[row_indexer,col_indexer] = value
invés".
Já estou fazendo isso, parece que tem um bichinho. Eu uso o Jupyter.
Obrigado! :)
pd.show_versions()
commit: Nenhum
python: 3.6.1.final.0
python-bits: 64
SO: Windows
Versão do sistema operacional: 8.1
máquina: AMD64
processador: Intel64 Family 6 Model 61 Stepping 4, GenuineIntel
byteorder: pouco
LC_ALL: Nenhum
LANG: Nenhum
LOCALE: Nenhum. Nenhum
pandas: 0,20,1
pytest: 3.0.7
pip: 9.0.1
ferramentas de configuração: 35.0.2
Cython: 0.25.2
numpy: 1.12.1
scipy: 0.19.0
xarray: Nenhum
IPython: 5.3.0
esfinge: 1.5.6
patsy: 0.4.1
dateutil: 2.6.0
pytz: 2017.2
blosc: nenhum
gargalo: 1.2.1
tabelas: 3.2.2
numexpr: 2.6.2
pena: nenhuma
matplotlib: 2.0.2
openpyxl: Nenhum
xlrd: 1.0.0
xlwt: 1.2.0
xlsxwriter: 0.9.6
lxml: 3.7.3
bs4: 4.6.0
html5lib: 0,999
sqlalchemy: 1.1.9
pymysql: Nenhum
psycopg2: Nenhum
jinja2: 2.9.6
s3fs: nenhum
pandas_gbq: Nenhum
pandas_datareader: Nenhum
@NadiaRom Você pode fornecer um exemplo completo? É difícil dizer com certeza, mas suspeito que df
veio de uma operação que pode ser uma visualização ou cópia. Por exemplo:
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
Portanto, estamos atualizando df1
corretamente. A ambigüidade é se df
também será atualizado ou não. Acho que algo semelhante está acontecendo com você, mas sem um exemplo reproduzível é difícil dizer com certeza.
@TomAugspurger Aqui está o código, em geral, eu nunca atribuo valores a pandas sem .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
Obrigado pela resposta rápida!
O problema aqui é que você está dividindo o dataframe primeiro com .loc
na linha 4. A tentativa de atribuir valores a essa fatia.
df_c = df.loc[df.encountry == country, :]
O Pandas não tem 100% de certeza se deseja atribuir valores apenas à sua fatia df_c
ou que ela se propague de volta até a df
. Para evitar isso, quando você atribuir df_c
certifique-se de dizer aos pandas que é seu próprio quadro de dados (e não uma fatia) usando
df_c = df.loc[df.encountry == country, :].copy()
Fazer isso corrigirá seu erro. Vou acrescentar um breve exemplo para ajudar a explicar o acima, pois percebi que muitos usuários ficam confusos com os pandas nesse aspecto.
>>> 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
Portanto, o procedimento acima funciona como esperamos! Agora vamos tentar um exemplo que reflete o que você tentou fazer com seus dados.
>>> 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
Parece que encontramos o mesmo erro! Mas mudou df_q
como esperávamos! Isso ocorre porque df_q
é uma fatia de df
, então, embora estejamos usando .loc [] df_q
pandas está nos avisando que não irá propagar as mudanças para cima para df
. Para evitar isso, precisamos ser mais explícitos e dizer que df_q
é seu próprio dataframe, separado de df
por declarar explicitamente assim.
Vamos começar de volta de df_q
mas use .copy()
desta vez.
>>> 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
Isso funciona sem erros porque dissemos aos pandas que df_q
é diferente de df
Se você de fato deseja que essas mudanças em df_c
se propaguem até df
é um outro ponto e responderá se você quiser.
@CRiddler Ótimo, obrigado !
Como você mencionou, .loc
cadeia nunca retornou resultados inesperados. Pelo que entendi, .copy()
garante aos Pandas que trataremos os df_sliced_once
selecionados como objetos separados e não pretendemos alterar totalmente df
iniciais. Por favor, corrija se eu confundi smth.
a documentação está aqui http://pandas.pydata.org/pandas-docs/stable/indexing.html#returning -a-view-versus-a-copy e @CRiddler tem uma bela explicação. em geral, você NÃO deve usar inplace
.
Se você de fato deseja que essas mudanças em
df_c
se propaguem atédf
é outro ponto inteiramente e responderá se você quiser.
@CRiddler Obrigado, sua resposta é melhor do que as que você poderia adicionar no Stack Overflow quando quiser propagar para o dataframe inicial ou dar uma indicação de como isso é feito?
@persep Em geral, não gosto de transformar problemas em threads de stackoverflow para obter ajuda, mas parece que esse problema recebeu um pouco de atenção desde a última postagem, então irei em frente e postarei meu método de lidar com esse tipo de problema em pandas. Eu normalmente faço isso não subdividindo o dataframe em variáveis separadas, mas, em vez disso, transformo as máscaras em variáveis - em seguida, combino as máscaras conforme necessário e defino os valores com base nessas máscaras para garantir que as mudanças aconteçam no dataframe original, e não em alguma cópia flutuante .
Dados originais:
>>>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
Lembre-se de que a criação de um dataframe temporário NÃO propagará as alterações
Conforme mostrado no exemplo anterior, isso faz alterações em apenas df_q
e gera um aviso de pandas (não copiado / colado aqui). E NÃO propaga nenhuma mudança para 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
Até onde sei, não há como usar o mesmo código acima e forçar a propagação das alterações de volta ao dataframe original.
No entanto, se mudarmos um pouco nosso pensamento e trabalharmos com máscaras em vez de subconjuntos completos, poderemos alcançar o resultado desejado. Embora isso não esteja necessariamente "propagando" alterações para o dataframe original de um subconjunto, estamos garantindo que todas as alterações que fizermos aconteçam no dataframe original df
. Para fazer isso, primeiro criamos as máscaras e, em seguida, as aplicamos quando queremos fazer uma alteração nesse subconjunto de 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
Por último, se quisermos ver como df_q ficaria, sempre podemos subconjuí-lo do dataframe original usando nosso q_mask
>>> df.loc[q_mask, :]
A B new_col
0 1 Q hello
1 2 Q hello
2 3 Q NaN
Embora isso não esteja necessariamente "propagando" as mudanças de df_q
para df
obtemos o mesmo resultado. A propagação real precisaria ser feita explicitamente e seria menos eficiente do que apenas trabalhar com máscaras.
@CRiddler Obrigado, você foi muito útil
Comentários muito úteis
O problema aqui é que você está dividindo o dataframe primeiro com
.loc
na linha 4. A tentativa de atribuir valores a essa fatia.O Pandas não tem 100% de certeza se deseja atribuir valores apenas à sua fatia
df_c
ou que ela se propague de volta até adf
. Para evitar isso, quando você atribuirdf_c
certifique-se de dizer aos pandas que é seu próprio quadro de dados (e não uma fatia) usandoFazer isso corrigirá seu erro. Vou acrescentar um breve exemplo para ajudar a explicar o acima, pois percebi que muitos usuários ficam confusos com os pandas nesse aspecto.
Exemplo com dados inventados
Portanto, o procedimento acima funciona como esperamos! Agora vamos tentar um exemplo que reflete o que você tentou fazer com seus dados.
Parece que encontramos o mesmo erro! Mas mudou
df_q
como esperávamos! Isso ocorre porquedf_q
é uma fatia dedf
, então, embora estejamos usando .loc []df_q
pandas está nos avisando que não irá propagar as mudanças para cima paradf
. Para evitar isso, precisamos ser mais explícitos e dizer quedf_q
é seu próprio dataframe, separado dedf
por declarar explicitamente assim.Vamos começar de volta de
df_q
mas use.copy()
desta vez.Isso funciona sem erros porque dissemos aos pandas que
df_q
é diferente dedf
Se você de fato deseja que essas mudanças em
df_c
se propaguem atédf
é um outro ponto e responderá se você quiser.