Hintergrund
Ich habe gerade meine Pandas von 0.11 auf 0.13.0rc1 aktualisiert. Jetzt gibt die Anwendung viele neue Warnungen aus. Einer von ihnen mag diesen:
E:\FinReporter\FM_EXT.py:449: 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
quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
Ich möchte wissen, was es genau bedeutet? Muss ich etwas ändern?
Wie soll ich die Warnung aussetzen, wenn ich darauf bestehe, sie zu verwenden quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
?
Die Funktion, die Fehler gibt
def _decode_stock_quote(list_of_150_stk_str):
"""decode the webpage and return dataframe"""
from cStringIO import StringIO
str_of_all = "".join(list_of_150_stk_str)
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]]
quote_df['TClose'] = quote_df['TPrice']
quote_df['RT'] = 100 * (quote_df['TPrice']/quote_df['TPCLOSE'] - 1)
quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
quote_df['TAmt'] = quote_df['TAmt']/TAMT_SCALE
quote_df['STK_ID'] = quote_df['STK'].str.slice(13,19)
quote_df['STK_Name'] = quote_df['STK'].str.slice(21,30)#.decode('gb2312')
quote_df['TDate'] = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])
return quote_df
Weitere Fehlermeldungen
E:\FinReporter\FM_EXT.py:449: 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
quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
E:\FinReporter\FM_EXT.py:450: 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
quote_df['TAmt'] = quote_df['TAmt']/TAMT_SCALE
E:\FinReporter\FM_EXT.py:453: 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
quote_df['TDate'] = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])
python
pandas
dataframe
chained-assignment
großer Käfer
quelle
quelle
df.set_value
Dokumente hier verwenden - pandas.pydata.org/pandas-docs/stable/generated/…df.set_value
ist veraltet. Pandas empfiehlt jetzt die Verwendung von.at[]
oder.iat[]
stattdessen. Dokumente hier pandas.pydata.org/pandas-docs/stable/generated/…option_context
hat: pandas.pydata.org/pandas-docs/stable/user_guide/options.html , verwenden alswith pd.option_context("mode.chained_assignment", None): [...]
Antworten:
Das
SettingWithCopyWarning
wurde erstellt, um potenziell verwirrende "verkettete" Zuweisungen wie die folgenden zu kennzeichnen, die nicht immer wie erwartet funktionieren, insbesondere wenn die erste Auswahl eine Kopie zurückgibt . [ Hintergrunddiskussion siehe GH5390 und GH5597 .]Die Warnung bietet einen Vorschlag zum Umschreiben wie folgt:
Dies passt jedoch nicht zu Ihrer Verwendung. Dies entspricht:
Es ist zwar klar, dass es Ihnen egal ist, ob Schreibvorgänge zum ursprünglichen Frame zurückkehren (da Sie den Verweis darauf überschreiben), aber dieses Muster kann leider nicht vom ersten verketteten Zuweisungsbeispiel unterschieden werden. Daher die (falsch positive) Warnung. Das Potenzial für Fehlalarme wird in den Dokumenten zur Indizierung angesprochen , wenn Sie weiterlesen möchten. Sie können diese neue Warnung mit der folgenden Zuordnung sicher deaktivieren.
quelle
.ix
, verbessert.iloc
usw.) können definitiv als "der primäre Weg" angesehen werden, ohne alle ununterbrochen vor anderen Wegen zu warnen. Lass sie stattdessen Erwachsene sein und wenn sie verkettete Aufgaben erledigen wollen, dann sei es so. Meine zwei Cent sowieso. Man sieht hier oft verärgerte Kommentare von Pandas-Entwicklern, wenn verkettete Aufgaben zur Lösung eines Problems beitragen, aber nicht als "primärer" Weg dazu angesehen werden.pd.options.mode.chained_assignment = None
hat, dass mein Code ungefähr sechsmal schneller ausgeführt wurde. Hat sonst noch jemand ähnliche Ergebnisse erzielt?Dieser Beitrag ist für Leser gedacht, die,
Installieren
Was ist der
SettingWithCopyWarning
?Um zu wissen, wie man mit dieser Warnung umgeht, ist es wichtig zu verstehen, was sie bedeutet und warum sie überhaupt ausgelöst wird.
Beim Filtern von DataFrames kann ein Frame geschnitten / indiziert werden, um entweder eine Ansicht oder eine Kopie zurückzugeben je nach internem Layout und verschiedenen Implementierungsdetails . Eine "Ansicht" ist, wie der Begriff andeutet, eine Ansicht in die Originaldaten, so dass das Ändern der Ansicht das ursprüngliche Objekt ändern kann. Andererseits ist eine "Kopie" eine Replikation von Daten aus dem Original, und das Ändern der Kopie hat keine Auswirkungen auf das Original.
Wie in anderen Antworten erwähnt,
SettingWithCopyWarning
wurde das erstellt, um "verkettete Zuweisungs" -Operationen zu kennzeichnen. Betrachten Siedf
im obigen Setup. Angenommen, Sie möchten alle Werte in Spalte "B" auswählen, wobei die Werte in Spalte "A"> 5 sind. Mit Pandas können Sie dies auf verschiedene Arten tun, von denen einige korrekter sind als andere. Zum Beispiel,Und,
Diese geben das gleiche Ergebnis zurück. Wenn Sie also nur diese Werte lesen, spielt dies keine Rolle. Also, was ist das Problem? Das Problem bei der verketteten Zuweisung besteht darin, dass es im Allgemeinen schwierig ist, vorherzusagen, ob eine Ansicht oder eine Kopie zurückgegeben wird. Dies wird daher größtenteils zu einem Problem, wenn Sie versuchen, Werte zurückzuweisen. Um auf dem vorherigen Beispiel aufzubauen, betrachten Sie, wie dieser Code vom Interpreter ausgeführt wird:
Mit einem einzigen
__setitem__
Anruf andf
. OTOH, betrachten Sie diesen Code:Abhängig davon, ob
__getitem__
eine Ansicht oder eine Kopie zurückgegeben wurde,__setitem__
funktioniert der Vorgang möglicherweise nicht .Im Allgemeinen sollten Sie
loc
für die beschriftungsbasierte Zuweisung undiloc
für die ganzzahlige / positionsbasierte Zuweisung verwenden, da die Spezifikation garantiert, dass sie immer mit dem Original arbeiten. Zum Festlegen einer einzelnen Zelle sollten Sie außerdemat
und verwendeniat
.Weitere finden Sie in der Dokumentation .
Sagen Sie mir einfach, wie ich die Warnung unterdrücken kann!
Betrachten Sie eine einfache Operation in der Spalte "A" von
df
. Wenn Sie "A" auswählen und durch 2 teilen, wird die Warnung ausgelöst, aber der Vorgang funktioniert.Es gibt verschiedene Möglichkeiten, diese Warnung direkt zum Schweigen zu bringen:
Mach ein
deepcopy
Ändern
pd.options.mode.chained_assignment
kann eingestellt werden
None
,"warn"
oder"raise"
."warn"
ist die Standardeinstellung.None
unterdrückt die Warnung vollständig und"raise"
wirft einSettingWithCopyError
, wodurch verhindert wird , dass die Operation ausgeführt wird.@Peter Cotton hat in den Kommentaren eine nette Möglichkeit gefunden, den Modus (geändert von diesem Kern ) mit einem Kontextmanager nicht aufdringlich zu ändern , um den Modus nur so lange einzustellen, wie es erforderlich ist, und ihn auf den zurückzusetzen Originalzustand, wenn fertig.
Die Verwendung ist wie folgt:
Oder um die Ausnahme auszulösen
Das "XY-Problem": Was mache ich falsch?
In den meisten Fällen versuchen Benutzer, nach Möglichkeiten zu suchen, um diese Ausnahme zu unterdrücken, ohne vollständig zu verstehen, warum sie überhaupt ausgelöst wurde. Dies ist ein gutes Beispiel für ein XY-Problem , bei dem Benutzer versuchen, ein Problem "Y" zu lösen, das tatsächlich ein Symptom für ein tiefer verwurzeltes Problem "X" ist. Auf der Grundlage häufiger Probleme, auf die diese Warnung stößt, werden Fragen gestellt und anschließend Lösungen vorgestellt.
Falscher Weg, dies zu tun:
Richtiger Weg mit
loc
:Sie können dazu eine der folgenden Methoden verwenden.
Dies liegt wahrscheinlich wahrscheinlich an Code, der sich weiter oben in Ihrer Pipeline befindet. Hast du
df2
aus etwas Größerem erschaffen , wie? In diesem Fall gibt die boolesche Indizierung eine Ansicht zurück und
df2
verweist auf das Original. Was Sie tun müssen, istdf2
einer Kopie zuzuweisen :Dies liegt daran,
df2
dass eine Ansicht aus einer anderen Schnittoperation erstellt worden sein muss, zDie Lösung besteht darin, entweder wie zuvor eine
copy()
zu verwendendf
oder zu verwendenloc
.quelle
Im Allgemeinen
SettingWithCopyWarning
geht es darum, Benutzern (und insbesondere neuen Benutzern) zu zeigen, dass sie möglicherweise eine Kopie bearbeiten und nicht das Original, wie sie denken. Es gibt Fehlalarme (IOW, wenn Sie wissen, was Sie tun, könnte es in Ordnung sein ). Eine Möglichkeit besteht darin, die Warnung (standardmäßig warnen ) einfach zu deaktivieren, wie von @Garrett vorgeschlagen.Hier ist eine weitere Option:
Sie können das
is_copy
Flag für dieses Objekt auf setzenFalse
, wodurch die Prüfung effektiv deaktiviert wird :Wenn Sie explizit kopieren, erfolgt keine weitere Warnung:
Der Code, den das OP oben anzeigt, ist zwar legitim und wahrscheinlich auch etwas, das ich tue, aber technisch gesehen ein Fall für diese Warnung und kein falsches Positiv. Eine weitere Möglichkeit, nicht die Warnung haben würde , die Auswahloperation zu tun über
reindex
, zBOder,
quelle
0.16
sehe ich viel mehr False Positives. Das Problem mit False Positives ist, dass man lernt, es zu ignorieren, obwohl es manchmal legitim ist.undefined
Verhalten. Wenn überhaupt, sollte es einen Fehler auslösen (um Fallstricke zu vermeidenC
), daapi
das aktuelle Warnverhalten für die Abwärtskompatibilität sinnvoll ist , da es eingefroren ist. Und ich werde sie werfen lassen, um sie als Fehler in meinem Produktionscode (warnings.filterwarnings('error', r'SettingWithCopyWarning
) zu fangen . Auch der Vorschlag,.loc
manchmal zu verwenden, hilft auch nicht (wenn es in einer Gruppe ist).Warnung zum Kopieren von Pandas-Datenrahmen
Wenn Sie so etwas tun:
pandas.ix
In diesem Fall wird ein neuer, eigenständiger Datenrahmen zurückgegeben.Alle Werte, die Sie in diesem Datenrahmen ändern möchten, ändern den ursprünglichen Datenrahmen nicht.
Darum versucht Pandas Sie zu warnen.
Warum
.ix
ist eine schlechte IdeeDas
.ix
Objekt versucht mehr als eine Sache zu tun, und für jeden, der etwas über sauberen Code gelesen hat, ist dies ein starker Geruch.Angesichts dieses Datenrahmens:
Zwei Verhaltensweisen:
Verhalten eins:
dfcopy
ist jetzt ein eigenständiger Datenrahmen. Das Ändern wird nicht geändertdf
Verhalten zwei: Dies ändert den ursprünglichen Datenrahmen.
Verwenden
.loc
stattdessenDie Pandas-Entwickler erkannten, dass das
.ix
Objekt [spekulativ] ziemlich stinkend war, und erstellten daher zwei neue Objekte, die beim Zugriff und bei der Zuweisung von Daten helfen. (Das andere Wesen.iloc
).loc
ist schneller, weil nicht versucht wird, eine Kopie der Daten zu erstellen..loc
soll Ihren vorhandenen Datenrahmen an Ort und Stelle ändern, was speichereffizienter ist..loc
ist vorhersehbar, es hat ein Verhalten.Die Lösung
In Ihrem Codebeispiel laden Sie eine große Datei mit vielen Spalten und ändern sie dann so, dass sie kleiner ist.
Die
pd.read_csv
Funktion kann Ihnen dabei helfen und das Laden der Datei erheblich beschleunigen.Also anstatt dies zu tun
Mach das
Dadurch werden nur die Spalten gelesen, an denen Sie interessiert sind, und sie werden ordnungsgemäß benannt. Keine Notwendigkeit, das böse
.ix
Objekt zu benutzen, um magische Dinge zu tun.quelle
.iloc
. Dies sind die beiden Hauptmethoden zum Indizieren von Pandas-Datenstrukturen. Lesen Sie mehr in der Dokumentation.Hier beantworte ich die Frage direkt. Wie man damit umgeht?
Machen Sie eine,
.copy(deep=False)
nachdem Sie schneiden. Siehe pandas.DataFrame.copy .Warten Sie, gibt ein Slice keine Kopie zurück? Immerhin versucht dies die Warnmeldung zu sagen? Lesen Sie die lange Antwort:
Dies gibt eine Warnung:
Das tut nicht:
Beide
df0
unddf1
sindDataFrame
Objekte, aber etwas an ihnen ist anders, sodass Pandas die Warnung drucken können. Lassen Sie uns herausfinden, was es ist.Wenn Sie das Diff-Tool Ihrer Wahl verwenden, werden Sie feststellen, dass der einzige wesentliche Unterschied über einige Adressen hinaus der folgende ist:
Die Methode, die entscheidet, ob gewarnt werden soll, ist
DataFrame._check_setitem_copy
die Prüfung_is_copy
. Also los geht's. Machen Sie einencopy
so, dass Ihr DataFrame nicht ist_is_copy
.Die Warnung schlägt vor, sie zu verwenden
.loc
. Wenn Sie sie jedoch.loc
für einen Frame verwenden_is_copy
, wird immer noch dieselbe Warnung angezeigt. Irreführend? Ja. Nervig? Sie wetten. Hilfreich? Möglicherweise, wenn eine verkettete Zuordnung verwendet wird. Die Kettenzuordnung kann jedoch nicht korrekt erkannt werden, und die Warnung wird wahllos gedruckt.quelle
Dieses Thema ist wirklich verwirrend mit Pandas. Zum Glück hat es eine relativ einfache Lösung.
Das Problem ist, dass nicht immer klar ist, ob Datenfiltervorgänge (z. B. loc) eine Kopie oder eine Ansicht des DataFrame zurückgeben. Die weitere Verwendung eines solchen gefilterten DataFrame könnte daher verwirrend sein.
Die einfache Lösung lautet (es sei denn, Sie müssen mit sehr großen Datenmengen arbeiten):
Stellen Sie immer sicher, dass Sie den DataFrame vor der Zuweisung implizit kopieren, wenn Sie Werte aktualisieren müssen.
quelle
Um jeden Zweifel auszuräumen, bestand meine Lösung darin, eine tiefe Kopie des Slice anstelle einer regulären Kopie zu erstellen. Dies ist je nach Kontext möglicherweise nicht anwendbar (Speicherbeschränkungen / Größe des Slice, potenzielle Leistungseinbußen - insbesondere, wenn die Kopie in einer Schleife wie bei mir usw. erfolgt).
Um klar zu sein, hier ist die Warnung, die ich erhalten habe:
Illustration
Ich hatte Zweifel, dass die Warnung wegen einer Spalte ausgelöst wurde, die ich auf eine Kopie des Slice fallen ließ. Obwohl technisch nicht versucht wurde, einen Wert in der Kopie des Slice festzulegen, war dies dennoch eine Modifikation der Kopie des Slice. Im Folgenden sind die (vereinfachten) Schritte aufgeführt, die ich unternommen habe, um den Verdacht zu bestätigen. Ich hoffe, dass dies denjenigen von uns hilft, die versuchen, die Warnung zu verstehen.
Beispiel 1: Das Löschen einer Spalte auf dem Original wirkt sich auf die Kopie aus
Das wussten wir schon, aber das ist eine gesunde Erinnerung. Das ist nicht , was die Warnung ist.
Es ist möglich, Änderungen an df1 zu vermeiden, die sich auf df2 auswirken
Beispiel 2: Das Löschen einer Spalte auf der Kopie kann sich auf das Original auswirken
Dies veranschaulicht tatsächlich die Warnung.
Es ist möglich, Änderungen an df2 zu vermeiden, die sich auf df1 auswirken
Prost!
quelle
Das sollte funktionieren:
quelle
Einige möchten die Warnung möglicherweise einfach unterdrücken:
quelle
Wenn Sie das Slice einer Variablen zugewiesen haben und die Variable wie folgt festlegen möchten:
Und Sie möchten Jeffs Lösung nicht verwenden, weil Ihr Condition Computing
df2
zu lang ist oder aus einem anderen Grund, dann können Sie Folgendes verwenden:df2.index.tolist()
Gibt die Indizes aller Einträge in df2 zurück, die dann verwendet werden, um Spalte B im ursprünglichen Datenrahmen festzulegen.quelle
Für mich trat dieses Problem in einem folgenden> vereinfachten <Beispiel auf. Und ich konnte es auch lösen (hoffentlich mit einer richtigen Lösung):
alter Code mit Warnung:
Dies druckte die Warnung für die Zeile
old_row[field] = new_row[field]
Da die Zeilen in der update_row-Methode tatsächlich vom Typ sind
Series
, habe ich die Zeile durch Folgendes ersetzt:dh Methode für den Zugriff / die Suche nach a
Series
. Obwohl beide einwandfrei funktionieren und das Ergebnis gleich ist, muss ich die Warnungen auf diese Weise nicht deaktivieren (= sie für andere Probleme bei der Kettenindizierung an einem anderen Ort aufbewahren).Ich hoffe das kann jemandem helfen.
quelle
Sie könnten das ganze Problem so vermeiden, glaube ich:
Verwenden von Zuweisen. Aus der Dokumentation : Weisen Sie einem DataFrame neue Spalten zu und geben Sie ein neues Objekt (eine Kopie) mit allen ursprünglichen Spalten zusätzlich zu den neuen zurück.
Siehe Tom Augspurgers Artikel über die Verkettung von Methoden in Pandas: https://tomaugspurger.github.io/method-chaining
quelle
Follow-up Anfänger Frage / Bemerkung
Vielleicht eine Klarstellung für andere Anfänger wie mich (ich komme aus R, was unter der Haube etwas anders zu funktionieren scheint). Der folgende harmlos aussehende und funktionale Code erzeugte weiterhin die SettingWithCopy-Warnung, und ich konnte nicht herausfinden, warum. Ich hatte die mit "Chained Indexing" ausgegebene Ausgabe gelesen und verstanden, aber mein Code enthält keine:
Aber später, viel zu spät, habe ich mir angesehen, wo die Funktion plot () heißt:
"Df" ist also kein Datenrahmen, sondern ein Objekt, das sich irgendwie daran erinnert, dass es durch Indizieren eines Datenrahmens erstellt wurde (ist das also eine Ansicht?), Der die Linie in plot () bilden würde.
gleichwertig
Das ist eine verkettete Indizierung. Habe ich das richtig verstanden?
Wie auch immer,
repariert.
quelle
Da diese Frage in den vorhandenen Antworten bereits vollständig erklärt und diskutiert wurde, werde ich
pandas
dem Kontextmanager nur einen übersichtlichen Ansatz geben, indem erpandas.option_context
(Links zu Dokumenten und Beispiel) verwendet ). Es ist absolut nicht erforderlich, eine benutzerdefinierte Klasse mit allen Dunder-Methoden und anderen Glocken zu erstellen und pfeift.Zuerst der Kontextmanager-Code selbst:
Dann ein Beispiel:
Bemerkenswert ist, dass beide Ansätze nicht geändert werden
a
, was für mich etwas überraschend ist, und sogar eine flache df-Kopie mit.copy(deep=False)
würde verhindern, dass diese Warnunga
ausgelöst wird (soweit ich verstehe, sollte sich auch eine flache Kopie zumindest ändern , aber dies ist nicht der Fall 't.pandas
Magie.).quelle
Ich hatte dieses Problem
.apply()
beim Zuweisen eines neuen Datenrahmens aus einem bereits vorhandenen Datenrahmen, für den ich die.query()
Methode verwendet habe. Zum Beispiel:Würde diesen Fehler zurückgeben. Das Update, das den Fehler in diesem Fall zu beheben scheint, besteht darin, dies zu ändern in:
Dies ist jedoch NICHT effizient, insbesondere wenn große Datenrahmen verwendet werden, da eine neue Kopie erstellt werden muss.
Wenn Sie die
.apply()
Methode zum Generieren einer neuen Spalte und ihrer Werte verwenden, können Sie den Fehler beheben, indem Sie Folgendes hinzufügen.reset_index(drop=True)
:quelle