Exemple de code
# My code
df.loc[0, 'column_name'] = 'foo bar'
Ce code dans Pandas 20.3 lance SettingWithCopyWarning et suggère de
"Essayez d'utiliser .loc[row_indexer,col_indexer] = value
place".
Je le fais déjà, on dirait qu'il y a un petit bug. J'utilise Jupyter.
Je vous remercie! :)
pd.show_versions()
commit: aucun
python: 3.6.1.final.0
bits python: 64
OS: Windows
Version du système d'exploitation: 8.1
machine: AMD64
Processeur: Intel64 Family 6 Model 61 Stepping 4, Genuine Intel
byteorder: petit
LC_ALL: Aucun
LANG: Aucun
LOCALE: Aucun. Aucun
pandas: 0.20.1
pytest: 3.0.7
pip: 9.0.1
setuptools: 35.0.2
Cython: 0,25,2
numpy: 1.12.1
scipy: 0.19.0
xarray: Aucun
IPython: 5.3.0
sphinx: 1,5,6
patsy: 0,4,1
dateutil: 2.6.0
pytz: 2017.2
blosc: Aucun
goulot d'étranglement: 1.2.1
tableaux: 3.2.2
numexpr: 2.6.2
plume: Aucune
matplotlib: 2.0.2
openpyxl: Aucun
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: Aucun
psycopg2: Aucun
jinja2: 2.9.6
s3fs: Aucun
pandas_gbq: Aucun
pandas_datareader: Aucun
@NadiaRom Pouvez-vous donner un exemple complet? C'est difficile à dire avec certitude, mais je soupçonne que df
provient d'une opération qui peut être une vue ou une copie. Par exemple:
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
Nous mettons donc à jour correctement df1
. L'ambiguïté est de savoir si df
sera également mis à jour. Je pense qu'une chose similaire vous arrive, mais sans un exemple reproductible, c'est difficile à dire avec certitude.
@TomAugspurger Voici le code, en général, je n'attribue jamais de valeurs aux pandas sans .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
Merci pour la réponse rapide!
Le problème ici est que vous découpez d'abord votre dataframe avec .loc
à la ligne 4. La tentative d'attribution de valeurs à cette tranche.
df_c = df.loc[df.encountry == country, :]
Pandas n'est pas sûr à 100% si vous voulez attribuer des valeurs uniquement à votre tranche df_c
, ou la faire propager jusqu'au df
. Pour éviter cela lorsque vous affectez pour la première fois df_c
assurez-vous de dire aux pandas qu'il s'agit de sa propre trame de données (et non d'une tranche) en utilisant
df_c = df.loc[df.encountry == country, :].copy()
Cela corrigera votre erreur. Je vais vous donner un bref exemple pour vous aider à expliquer ce qui précède, car j'ai remarqué que de nombreux utilisateurs étaient confus par les pandas à cet égard.
>>> 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
Donc, ce qui précède fonctionne comme prévu! Essayons maintenant un exemple qui reflète ce que vous avez tenté de faire avec vos données.
>>> 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
On dirait que nous avons rencontré la même erreur! Mais cela a changé df_q
comme prévu! C'est parce que df_q
est une tranche de df
donc, même si nous utilisons .loc [] df_q
pandas nous avertit qu'il ne propagera pas les changements à df
. Pour éviter cela, nous devons être plus explicites et dire que df_q
est son propre dataframe, séparé de df
en le déclarant explicitement.
Commençons à partir de df_q
mais utilisez .copy()
cette fois.
>>> 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
Cela fonctionne sans erreur car nous avons dit aux pandas que df_q
est séparé de df
Si vous voulez en fait que ces modifications de df_c
se propagent jusqu'à df
c'est un tout autre point et vous répondra si vous le souhaitez.
@CRiddler Super, merci !
Comme vous l'avez mentionné, chained .loc
n'a jamais renvoyé de résultats inattendus. Si je comprends bien, .copy()
garantit aux Pandas que nous traitons les df_sliced_once
comme des objets séparés et que nous n'avons pas l'intention de changer le df
initial. Veuillez corriger si j'ai confondu smth.
la documentation est ici http://pandas.pydata.org/pandas-docs/stable/indexing.html#returning -a-view-versus-a-copy et @CRiddler a une belle expl. vous ne devriez en général PAS utiliser du tout inplace
.
Si vous voulez en fait que ces modifications de
df_c
se propagent jusqu'àdf
c'est un tout autre point et vous répondra si vous le souhaitez.
@CRiddler Merci, votre réponse est meilleure que celles de Stack Overflow. Pourriez-vous ajouter lorsque vous souhaitez propager vers le dataframe initial ou donner une indication de la façon dont cela est fait?
@persep En général, je n'aime pas transformer les problèmes en threads stackoverflow pour obtenir de l'aide, mais il semble que ce problème ait attiré un peu d'attention depuis la dernière publication, je vais donc publier ma méthode pour résoudre ce type de problème pandas. Je fais généralement cela en ne sous-traitant pas la trame de données en variables séparées, mais je transforme plutôt les masques en variables - puis je combine les masques au besoin et je définis des valeurs en fonction de ces masques pour m'assurer que les modifications se produisent dans la trame de données d'origine, et non à une copie flottante. .
Données d'origine:
>>>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
N'oubliez pas que la création d'un dataframe temporaire ne propagera PAS les modifications
Comme indiqué dans l'exemple précédent, cela ne modifie que df_q
et déclenche un avertissement pandas (non copié / collé ici). ET ne propage PAS de modifications à 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
À ma connaissance, il n'y a aucun moyen d'utiliser le même code que ci-dessus et de forcer les modifications à se propager vers le dataframe d'origine.
Cependant, si nous changeons un peu notre façon de penser et que nous travaillons avec des masques au lieu de sous-ensembles complets, nous pouvons obtenir le résultat souhaité. Bien qu'il ne s'agisse pas nécessairement de "propager" les modifications à la trame de données d'origine à partir d'un sous-ensemble, nous nous assurons que toutes les modifications que nous apportons se produiront dans la trame df
données d'origine 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
Enfin, si nous voulions voir à quoi ressemblerait df_q, nous pouvons toujours le sous-définir à partir du dataframe d'origine en utilisant notre q_mask
>>> df.loc[q_mask, :]
A B new_col
0 1 Q hello
1 2 Q hello
2 3 Q NaN
Bien que cela ne "propage" pas nécessairement les changements de df_q
à df
nous obtenons le même résultat. La propagation réelle devrait être faite explicitement et serait moins efficace que de simplement travailler avec des masques.
@CRiddler Merci, vous avez été très utile
Commentaire le plus utile
Le problème ici est que vous découpez d'abord votre dataframe avec
.loc
à la ligne 4. La tentative d'attribution de valeurs à cette tranche.Pandas n'est pas sûr à 100% si vous voulez attribuer des valeurs uniquement à votre tranche
df_c
, ou la faire propager jusqu'audf
. Pour éviter cela lorsque vous affectez pour la première foisdf_c
assurez-vous de dire aux pandas qu'il s'agit de sa propre trame de données (et non d'une tranche) en utilisantCela corrigera votre erreur. Je vais vous donner un bref exemple pour vous aider à expliquer ce qui précède, car j'ai remarqué que de nombreux utilisateurs étaient confus par les pandas à cet égard.
Exemple avec des données constituées
Donc, ce qui précède fonctionne comme prévu! Essayons maintenant un exemple qui reflète ce que vous avez tenté de faire avec vos données.
On dirait que nous avons rencontré la même erreur! Mais cela a changé
df_q
comme prévu! C'est parce quedf_q
est une tranche dedf
donc, même si nous utilisons .loc []df_q
pandas nous avertit qu'il ne propagera pas les changements àdf
. Pour éviter cela, nous devons être plus explicites et dire quedf_q
est son propre dataframe, séparé dedf
en le déclarant explicitement.Commençons à partir de
df_q
mais utilisez.copy()
cette fois.Cela fonctionne sans erreur car nous avons dit aux pandas que
df_q
est séparé dedf
Si vous voulez en fait que ces modifications de
df_c
se propagent jusqu'àdf
c'est un tout autre point et vous répondra si vous le souhaitez.