Ich habe gerade mein Pandas von 0.11 auf 0.13.0rc1 aktualisiert. Jetzt gibt die Anwendung viele neue Warnungen aus. Einer von ihnen wie diese:
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 das genau bedeutet? Muss ich etwas ändern?
Wie kann ich die Warnung aufheben, wenn ich darauf bestehe, quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
zu verwenden?
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
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])
Die SettingWithCopyWarning
wurde erstellt, um potentiell verwirrende "verkettete" Zuweisungen, wie die folgenden, zu kennzeichnen, die nicht immer wie erwartet funktionieren, besonders wenn die erste Auswahl eine Kopie zurückgibt. [siehe GH5390 und GH5597 für eine Hintergrunddiskussion].
df[df['A'] > 2]['B'] = new_val # new_val not set in df
Die Warnung bietet einen Vorschlag, wie folgt umzuschreiben:
df.loc[df['A'] > 2, 'B'] = new_val
Dies passt jedoch nicht zu Ihrer Verwendung, die gleichbedeutend ist mit:
df = df[df['A'] > 2]
df['B'] = new_val
Während es klar ist, dass Sie sich nicht darum kümmern, dass Schreibvorgänge zum ursprünglichen Frame zurückkehren (da Sie den Verweis darauf überschrieben haben), kann dieses Muster leider nicht von dem ersten verketteten Zuweisungsbeispiel unterschieden werden, daher die (falsch positive) Warnung. Das Potenzial für Fehlalarme wird in den docs on indexing angesprochen, wenn Sie mehr darüber lesen möchten. Sie können diese neue Warnung mit der folgenden Zuweisung sicher deaktivieren.
pd.options.mode.chained_assignment = None # default='warn'
Im Allgemeinen ist der Sinn der SettingWithCopyWarning
, den Benutzern (und insbesondere neuen Benutzern) zu zeigen, dass sie möglicherweise mit einer Kopie arbeiten und nicht mit dem Original, wie sie denken. Es gibt falsch positive Ergebnisse (d.h. wenn man weiß, was man tut, kann es ok sein). Eine Möglichkeit ist, die (standardmäßige Warnung) Warnung zu deaktivieren, wie @Garrett vorschlägt.
Hier ist eine weitere Möglichkeit:
In [1]: df = DataFrame(np.random.randn(5, 2), columns=list('AB'))
In [2]: dfa = df.ix[:, [1, 0]]
In [3]: dfa.is_copy
Out[3]: True
In [4]: dfa['A'] /= 2
/usr/local/bin/ipython:1: 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
#!/usr/local/bin/python
Sie können das is_copy
-Flag auf False
setzen, was die Prüfung für dieses Objekt effektiv ausschaltet:
In [5]: dfa.is_copy = False
In [6]: dfa['A'] /= 2
Wenn Sie explizit kopieren, wird keine weitere Warnung ausgegeben:
In [7]: dfa = df.ix[:, [1, 0]].copy()
In [8]: dfa['A'] /= 2
Der oben gezeigte Code ist zwar legitim und wird wahrscheinlich auch von mir verwendet, ist aber technisch gesehen ein Fall für diese Warnung und kein Fehlalarm. Eine andere Möglichkeit, die Warnung nicht zu erhalten, wäre, die Auswahloperation über reindex
durchzuführen, z.B.
quote_df = quote_df.reindex(columns=['STK', ...])
Oder,
quote_df = quote_df.reindex(['STK', ...], axis=1) # v.0.21
Wenn Sie etwas wie das hier tun:
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
"pandas.ix" gibt in diesem Fall einen neuen, eigenständigen Datenrahmen zurück.
Alle Werte, die Sie in diesem Datenrahmen ändern, ändern nicht den ursprünglichen Datenrahmen.
Davor versucht Pandas Sie zu warnen.
.ix
eine schlechte Idee istDas .ix
-Objekt versucht, mehr als eine Sache zu tun, und für jeden, der etwas über sauberen Code gelesen hat, ist das ein starker Geruch.
Nehmen wir diesen Datenrahmen:
df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})
Zwei Verhaltensweisen:
dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2
Verhalten eins: dfcopy
ist jetzt ein eigenständiger Datenrahmen. Wenn man ihn ändert, wird df
nicht geändert.
df.ix[0, "a"] = 3
Verhalten zwei: Dies ändert den ursprünglichen Datenrahmen.
.loc
.Die Pandas-Entwickler haben erkannt, dass das .ix'-Objekt ziemlich stinkt [spekulativ] und haben daher zwei neue Objekte geschaffen, die bei der Aufnahme und Zuordnung von Daten helfen. (Das andere ist
.iloc`)
.loc
ist schneller, weil es nicht versucht, eine Kopie der Daten zu erstellen.
.loc
ist dazu gedacht, den vorhandenen Datenrahmen an Ort und Stelle zu verändern, was speichereffizienter ist.
.loc
ist vorhersehbar, es hat ein bestimmtes Verhalten.
In Ihrem Code-Beispiel laden Sie eine große Datei mit vielen Spalten und ändern sie dann so, dass sie kleiner wird.
Die Funktion "pd.read_csv" kann Ihnen dabei sehr helfen und auch das Laden der Datei erheblich beschleunigen.
Anstatt dies also zu tun
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]]
Tun Sie dies
columns = ['STK', 'TPrice', 'TPCLOSE', 'TOpen', 'THigh', 'TLow', 'TVol', 'TAmt', 'TDate', 'TTime']
df = pd.read_csv(StringIO(str_of_all), sep=',', usecols=[0,3,2,1,4,5,8,9,30,31])
df.columns = columns
Damit werden nur die Spalten gelesen, an denen Sie interessiert sind, und sie werden richtig benannt. Das böse "ix"-Objekt muss nicht verwendet werden, um magische Dinge zu tun.