Codebeispiel
# My code
df.loc[0, 'column_name'] = 'foo bar'
Dieser Code in Pandas 20.3 löst SettingWithCopyWarning aus und schlägt vor
"Versuchen Sie stattdessen .loc[row_indexer,col_indexer] = value
verwenden".
Ich mache das schon, es sieht so aus, als gäbe es einen kleinen Fehler. Ich benutze Jupyter.
Vielen Dank! :) :)
pd.show_versions()
Festschreiben: Keine
Python: 3.6.1.final.0
Python-Bits: 64
Betriebssystem: Windows
Betriebssystemversion: 8.1
Maschine: AMD64
Prozessor: Intel64 Family 6 Model 61 Stepping 4, GenuineIntel
Byteorder: wenig
LC_ALL: Keine
LANG: Keine
LOCALE: Keine
Pandas: 0,20,1
Pytest: 3.0.7
pip: 9.0.1
setuptools: 35.0.2
Cython: 0,25,2
Anzahl: 1.12.1
scipy: 0.19.0
xarray: Keine
IPython: 5.3.0
Sphinx: 1.5.6
Patsy: 0.4.1
Datum: 2.6.0
pytz: 2017.2
blosc: Keine
Engpass: 1.2.1
Tabellen: 3.2.2
numexpr: 2.6.2
Feder: Keine
matplotlib: 2.0.2
openpyxl: Keine
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: Keine
psycopg2: Keine
jinja2: 2.9.6
s3fs: Keine
pandas_gbq: Keine
pandas_datareader: Keine
@NadiaRom Können Sie ein vollständiges Beispiel liefern? Es ist schwer zu sagen, aber ich vermute, dass df
von einer Operation stammt, die eine Ansicht oder eine Kopie sein kann. Zum Beispiel:
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
Wir aktualisieren also df1
korrekt. Die Unklarheit besteht darin, ob df
ebenfalls aktualisiert wird oder nicht. Ich denke, Ihnen passiert etwas Ähnliches, aber ohne ein reproduzierbares Beispiel ist es schwer zu sagen.
@ TomAugspurger Hier ist der Code, im Allgemeinen weise ich Pandas ohne .loc niemals Werte zu
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
Vielen Dank für die schnelle Antwort!
Das Problem hierbei ist, dass Sie Ihren Datenrahmen zuerst mit .loc
in Zeile 4 aufteilen. Der Versuch, diesem Slice Werte zuzuweisen.
df_c = df.loc[df.encountry == country, :]
Pandas ist sich nicht 100% sicher, ob Sie nur Ihrem df_c
Slice Werte zuweisen oder es bis zum ursprünglichen df
möchten. Um dies zu vermeiden, wenn Sie df_c
zum ersten Mal zuweisen, stellen Sie sicher, dass Sie Pandas mitteilen, dass es sich um einen eigenen Datenrahmen (und nicht um ein Slice) handelt
df_c = df.loc[df.encountry == country, :].copy()
Dadurch wird Ihr Fehler behoben. Ich werde ein kurzes Beispiel anführen, um das oben Gesagte zu erklären, da ich festgestellt habe, dass viele Benutzer in diesem Aspekt von Pandas verwirrt sind.
>>> 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
Das oben genannte funktioniert also wie erwartet! Versuchen wir nun ein Beispiel, das widerspiegelt, was Sie mit Ihren Daten versucht haben.
>>> 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
Sieht so aus, als hätten wir den gleichen Fehler gemacht! Aber es hat df_q
geändert, wie wir erwartet hatten! Dies liegt daran, dass df_q
ein Stück von df
, obwohl wir .loc [] df_q
Pandas verwenden, warnt uns, dass es die Änderungen nicht weitergeben wird bis df
. Um dies zu vermeiden, müssen wir expliziter sein und sagen, dass df_q
ein eigener Datenrahmen ist, der von df
indem wir dies explizit deklarieren.
Beginnen wir wieder bei df_q
aber verwenden Sie diesmal .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
Dies funktioniert ohne Fehler, da wir Pandas mitgeteilt haben, dass df_q
von df
Wenn Sie tatsächlich möchten, dass diese Änderungen an df_c
bis zu df
weitergegeben werden, ist dies ein weiterer Punkt und wird beantwortet, wenn Sie möchten.
@ CRiddler Großartig, danke !
Wie Sie bereits erwähnt haben, hat verkettetes .loc
nie zu unerwarteten Ergebnissen geführt. Soweit ich weiß, stellt .copy()
Pandas sicher, dass wir ausgewählte df_sliced_once
als separates Objekt behandeln und nicht beabsichtigen, die anfänglichen vollen df
zu ändern. Bitte korrigieren Sie, wenn ich etw verwechselt habe.
Die Dokumentation finden Sie hier http://pandas.pydata.org/pandas-docs/stable/indexing.html#returning -a-view-versus-a-copy und @CRiddler hat eine nette Erklärung. Sie sollten inplace
im Allgemeinen NICHT verwenden.
Wenn Sie tatsächlich möchten, dass diese Änderungen an
df_c
bis zudf
weitergegeben werden, ist dies ein weiterer Punkt und wird beantwortet, wenn Sie möchten.
@CRiddler Vielen Dank, dass Ihre Antwort besser ist als die in Stack Overflow. Können Sie sie hinzufügen, wenn Sie sie an den ursprünglichen Datenrahmen weitergeben oder einen Hinweis darauf geben möchten, wie sie ausgeführt wird?
@persep Im Allgemeinen mag ich es nicht, Probleme in Stackoverflow-Threads umzuwandeln, um Hilfe zu erhalten, aber es scheint, dass dieses Problem seit dem letzten Posting einiges an Aufmerksamkeit erhalten hat, also werde ich meine Methode zur Behebung dieser Art von Problem in veröffentlichen Pandas. Normalerweise mache ich das, indem ich den Datenrahmen nicht in separate Variablen unterteile, sondern Masken in Variablen umwandle. Kombiniere dann Masken nach Bedarf und setze Werte basierend auf diesen Masken, um sicherzustellen, dass die Änderungen im ursprünglichen Datenrahmen und nicht in einer herumschwebenden Kopie stattfinden .
Originale Daten:
>>>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
Denken Sie daran, dass beim Erstellen eines temporären Datenrahmens KEINE Änderungen weitergegeben werden
Wie im vorherigen Beispiel gezeigt, werden nur Änderungen an df_q
und eine Pandas-Warnung ausgelöst (hier nicht kopiert / eingefügt). AND verbreitet KEINE Änderungen an 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
Meines Wissens gibt es keine Möglichkeit, denselben Code wie oben zu verwenden und Änderungen zu erzwingen, um zum ursprünglichen Datenrahmen zurückzukehren.
Wenn wir jedoch unser Denken ein wenig ändern und mit Masken anstelle von vollständigen Teilmengen arbeiten, können wir das gewünschte Ergebnis erzielen. Dies "überträgt" zwar nicht unbedingt Änderungen am ursprünglichen Datenrahmen aus einer Teilmenge, stellen jedoch sicher, dass alle Änderungen, die wir am ursprünglichen Datenrahmen vornehmen, df
. Dazu erstellen wir zuerst Masken und wenden sie dann an, wenn wir eine Änderung an dieser Teilmenge von df
vornehmen möchten
>>> 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
Wenn wir jemals sehen wollten, wie df_q aussehen würde, können wir es jederzeit mit unserem q_mask
vom ursprünglichen Datenrahmen unterteilen
>>> df.loc[q_mask, :]
A B new_col
0 1 Q hello
1 2 Q hello
2 3 Q NaN
Obwohl dies nicht unbedingt Änderungen von df_q
auf df
"propagiert", erzielen wir das gleiche Ergebnis. Die tatsächliche Weitergabe müsste explizit erfolgen und wäre weniger effizient als nur die Arbeit mit Masken.
@CRiddler Danke, du warst sehr hilfreich
Hilfreichster Kommentar
Das Problem hierbei ist, dass Sie Ihren Datenrahmen zuerst mit
.loc
in Zeile 4 aufteilen. Der Versuch, diesem Slice Werte zuzuweisen.Pandas ist sich nicht 100% sicher, ob Sie nur Ihrem
df_c
Slice Werte zuweisen oder es bis zum ursprünglichendf
möchten. Um dies zu vermeiden, wenn Siedf_c
zum ersten Mal zuweisen, stellen Sie sicher, dass Sie Pandas mitteilen, dass es sich um einen eigenen Datenrahmen (und nicht um ein Slice) handeltDadurch wird Ihr Fehler behoben. Ich werde ein kurzes Beispiel anführen, um das oben Gesagte zu erklären, da ich festgestellt habe, dass viele Benutzer in diesem Aspekt von Pandas verwirrt sind.
Beispiel mit erfundenen Daten
Das oben genannte funktioniert also wie erwartet! Versuchen wir nun ein Beispiel, das widerspiegelt, was Sie mit Ihren Daten versucht haben.
Sieht so aus, als hätten wir den gleichen Fehler gemacht! Aber es hat
df_q
geändert, wie wir erwartet hatten! Dies liegt daran, dassdf_q
ein Stück vondf
, obwohl wir .loc []df_q
Pandas verwenden, warnt uns, dass es die Änderungen nicht weitergeben wird bisdf
. Um dies zu vermeiden, müssen wir expliziter sein und sagen, dassdf_q
ein eigener Datenrahmen ist, der vondf
indem wir dies explizit deklarieren.Beginnen wir wieder bei
df_q
aber verwenden Sie diesmal.copy()
.Dies funktioniert ohne Fehler, da wir Pandas mitgeteilt haben, dass
df_q
vondf
Wenn Sie tatsächlich möchten, dass diese Änderungen an
df_c
bis zudf
weitergegeben werden, ist dies ein weiterer Punkt und wird beantwortet, wenn Sie möchten.