it-swarm.com.de

Wie gehe ich mit SettingWithCopyWarning in Pandas um?

Hintergrund

Ich habe gerade meine Pandas von 0.11 auf 0.13.0rc1 aktualisiert. Jetzt werden durch die Anwendung viele neue Warnungen ausgegeben. Einer von ihnen so:

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 genau es bedeutet. Muss ich etwas ändern?

Wie kann ich die Warnung aussetzen, wenn ich nicht auf quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE verzichten möchte?

Die Funktion, die Fehler ausgibt

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])
386
bigbug

Die Variable SettingWithCopyWarning wurde erstellt, um potenziell verwirrende "verkettete" Zuweisungen wie die folgenden zu kennzeichnen, die nicht immer wie erwartet funktionieren, insbesondere wenn die erste Auswahl ein copy zurückgibt. [siehe GH5390 und GH5597 für Hintergrundbesprechung.]

df[df['A'] > 2]['B'] = new_val  # new_val not set in df

Die Warnung bietet einen Vorschlag zum Umschreiben wie folgt:

df.loc[df['A'] > 2, 'B'] = new_val

Dies passt jedoch nicht zu Ihrer Verwendung, was gleichbedeutend ist mit:

df = df[df['A'] > 2]
df['B'] = new_val

Es ist zwar klar, dass es Ihnen nicht wichtig ist, dass Schreibvorgänge zum ursprünglichen Frame zurückkehren (da Sie den Verweis darauf überschrieben haben), dieses Muster kann jedoch leider nicht vom ersten verketteten Zuweisungsbeispiel unterschieden werden. Das Potenzial für Fehlalarme wird in den docs über die Indizierung angesprochen, wenn Sie weiter lesen möchten. Sie können diese neue Warnung mit folgender Zuordnung sicher deaktivieren.

pd.options.mode.chained_assignment = None  # default='warn'
512
Garrett

Im Allgemeinen besteht der Zweck von SettingWithCopyWarning darin, Benutzern (und insbesondere neuen Benutzern) zu zeigen, dass sie möglicherweise mit einer Kopie arbeiten und nicht mit dem originell wie sie denken. Es gibt falsch positive (IOW, wenn Sie wissen, was Sie tun, könnte es in Ordnung sein ). Eine Möglichkeit besteht darin, die Warnung einfach auszuschalten, wie von @Garrett vorgeschlagen.

Hier ist eine andere Option:

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, wodurch die Prüfung für dieses Objekt deaktiviert wird :

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 Code, den das OP oben anzeigt, ist zwar legitim und wahrscheinlich auch etwas, was ich tue, technisch gesehen ein Argument für diese Warnung und kein falsches Positiv. Eine andere Möglichkeit, die Warnung nicht zu haben , wäre, die Auswahloperation über reindex durchzuführen, z.

quote_df = quote_df.reindex(columns=['STK', ...])

Oder,

quote_df = quote_df.reindex(['STK', ...], axis=1)  # v.0.21
138
Jeff

Wie gehe ich mit SettingWithCopyWarning in Pandas um?

Dieser Beitrag richtet sich an Leser, die

  1. Würde gerne verstehen, was diese Warnung bedeutet
  2. Möchte verschiedene Möglichkeiten zur Unterdrückung dieser Warnung verstehen
  3. Ich möchte verstehen, wie man den Code verbessert und bewährte Methoden befolgt, um diese Warnung in Zukunft zu vermeiden.

Setup

np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (3, 5)), columns=list('ABCDE'))
df
   A  B  C  D  E
0  5  0  3  3  7
1  9  3  5  2  4
2  7  6  8  8  1

Was ist das SettingWithCopyWarning?

Um zu wissen, wie mit dieser Warnung umgegangen werden soll, ist es wichtig zu verstehen, was sie bedeutet und warum sie überhaupt ausgelöst wird.

Beim Filtern von DataFrames ist es möglich, einen Frame in Segmente zu unterteilen/zu indizieren, um je nach internem Layout und verschiedenen Implementierungsdetails entweder eine Ansicht oder eine Kopie zurückzugeben. Eine "Ansicht" ist, wie der Begriff andeutet, eine Ansicht in die ursprünglichen Daten, 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 bereits in anderen Antworten erwähnt, wurde SettingWithCopyWarning erstellt, um "verkettete Zuweisungsvorgänge" zu kennzeichnen. Betrachten Sie df 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,

df[df.A > 5]['B']

1    3
2    6
Name: B, dtype: int64

Und,

df.loc[df.A > 5, 'B']

1    3
2    6
Name: B, dtype: int64

Diese geben das gleiche Ergebnis zurück. Wenn Sie also nur diese Werte lesen, spielt dies keine Rolle. Worum geht es also? Das Problem bei der verketteten Zuweisung besteht darin, dass es im Allgemeinen schwierig ist, vorherzusagen, ob eine Ansicht oder eine Kopie zurückgegeben wird , sodass dies zum größten Teil zu einem Problem wird, wenn Sie versuchen, Werte zurückzuweisen. Um auf dem früheren Beispiel aufzubauen, überlegen Sie, wie dieser Code vom Interpreter ausgeführt wird:

df.loc[df.A > 5, 'B'] = 4
# becomes
df.__setitem__((df.A > 5, 'B'), 4)

Mit einem einzigen __setitem__ Aufruf von df. OTOH, betrachten Sie diesen Code:

df[df.A > 5]['B'] = 4
# becomes
df.__getitem__(df.A > 5).__setitem__('B", 4)

Abhängig davon, ob __getitem__ Eine Ansicht oder eine Kopie zurückgegeben hat, funktioniert die Operation __setitem__ möglicherweise nicht .

Im Allgemeinen sollten Sie loc für die kennsatzbasierte Zuweisung und iloc für die ganzzahlige/positionsbasierte Zuweisung verwenden, wie die Spezifikation garantiert dass sie immer auf dem Original arbeiten. Darüber hinaus sollten Sie zum Festlegen einer einzelnen Zelle at und iat verwenden.

Weitere finden Sie in der Dokumentation .

Hinweis
Alle booleschen Indexierungsoperationen, die mit loc ausgeführt werden, können auch mit iloc ausgeführt werden. Der einzige Unterschied besteht darin, dass iloc entweder Ganzzahlen/Positionen für den Index oder ein numpy-Array von Booleschen Werten und Ganzzahlen/Positionsindizes für die Spalten erwartet.

Zum Beispiel,

df.loc[df.A > 5, 'B'] = 4

Kann nas geschrieben werden

df.iloc[(df.A > 5).values, 1] = 4

Und,

df.loc[1, 'A'] = 100

Kann geschrieben werden als

df.iloc[1, 0] = 100

Und so weiter.


Sag mir einfach, wie ich die Warnung unterdrücken kann!

Betrachten Sie eine einfache Operation in der Spalte "A" von df. Durch Auswahl von "A" und Teilen durch 2 wird die Warnung ausgelöst, die Operation funktioniert jedoch.

df2 = df[['A']]
df2['A'] /= 2
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/__main__.py:1: 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

df2
     A
0  2.5
1  4.5
2  3.5

Es gibt verschiedene Möglichkeiten, diese Warnung direkt auszuschalten:

  1. Erstelle ein deepcopy

    df2 = df[['A']].copy(deep=True)
    df2['A'] /= 2
    
  2. pd.options.mode.chained_assignment Ändern
    Kann auf None, "warn" Oder "raise" Eingestellt werden. "warn" Ist die Standardeinstellung. None unterdrückt die Warnung vollständig und "raise" wirft ein SettingWithCopyError, wodurch verhindert wird, dass die Operation ausgeführt wird.

    pd.options.mode.chained_assignment = None
    df2['A'] /= 2
    

@ Peter Cotton hat in den Kommentaren eine nette Möglichkeit gefunden, den Modus (modifiziert von this Gist ) mit einem Kontextmanager nicht-intrusiv zu ändern, um nur den Modus einzustellen solange es erforderlich ist, und das Zurücksetzen auf den ursprünglichen Zustand, wenn es abgeschlossen ist.

class ChainedAssignent:
    def __init__(self, chained=None):
        acceptable = [None, 'warn', 'raise']
        assert chained in acceptable, "chained must be in " + str(acceptable)
        self.swcw = chained

    def __enter__(self):
        self.saved_swcw = pd.options.mode.chained_assignment
        pd.options.mode.chained_assignment = self.swcw
        return self

    def __exit__(self, *args):
        pd.options.mode.chained_assignment = self.saved_swcw

Die Verwendung ist wie folgt:

# some code here
with ChainedAssignent():
    df2['A'] /= 2
# more code follows

Oder um die Ausnahme auszulösen

with ChainedAssignent(chained='raise'):
    df2['A'] /= 2

SettingWithCopyError: 
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

Das "XY-Problem": Was mache ich falsch?

Häufig 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 eines tiefer verwurzelten Problems "X" ist. Es werden Fragen zu allgemeinen Problemen gestellt, auf die diese Warnung stößt, und anschließend werden Lösungen vorgestellt.

Frage 1
Ich habe einen DataFrame

df
       A  B  C  D  E
    0  5  0  3  3  7
    1  9  3  5  2  4
    2  7  6  8  8  1

Ich möchte Werte in Spalte "A"> 5 bis 1000 zuweisen. Meine erwartete Ausgabe ist

      A  B  C  D  E
0     5  0  3  3  7
1  1000  3  5  2  4
2  1000  6  8  8  1

Falsche Vorgehensweise:

df.A[df.A > 5] = 1000         # works, because df.A returns a view
df[df.A > 5]['A'] = 1000      # does not work
df.loc[df.A  5]['A'] = 1000   # does not work

Richtiger Weg mit loc:

df.loc[df.A > 5, 'A'] = 1000


Frage 21
Ich versuche, den Wert in Zelle (1, 'D') auf 12345 zu setzen. Meine erwartete Ausgabe ist

   A  B  C      D  E
0  5  0  3      3  7
1  9  3  5  12345  4
2  7  6  8      8  1

Ich habe verschiedene Möglichkeiten ausprobiert, auf diese Zelle zuzugreifen, z. B. df['D'][1]. Wie geht das am besten?

1. Diese Frage bezieht sich nicht speziell auf die Warnung, aber es ist gut zu verstehen, wie diese bestimmte Operation korrekt ausgeführt wird, um Situationen zu vermeiden, in denen die Warnung möglicherweise in Zukunft auftreten könnte.

Sie können hierzu eine der folgenden Methoden verwenden.

df.loc[1, 'D'] = 12345
df.iloc[1, 3] = 12345
df.at[1, 'D'] = 12345
df.iat[1, 3] = 12345


Frage 3
Ich versuche, Werte basierend auf einer Bedingung zu unterteilen. Ich habe einen DataFrame

   A  B  C  D  E
1  9  3  5  2  4
2  7  6  8  8  1

Ich möchte 123 Werte in "D" zuweisen, sodass "C" == 5. Ich habe es versucht

df2.loc[df2.C == 5, 'D'] = 123

Das scheint in Ordnung zu sein, aber ich bekomme immernoch das SettingWithCopyWarning! Wie behebe ich das?

Dies liegt wahrscheinlich an Code, der sich weiter oben in Ihrer Pipeline befindet. Haben Sie df2 Aus etwas Größerem erstellt?

df2 = df[df.A > 5]

? In diesem Fall gibt die boolesche Indizierung eine Ansicht zurück, sodass df2 Auf das Original verweist. Sie müssen lediglich df2 Einer Kopie zuweisen:

df2 = df[df.A > 5].copy()
# Or,
# df2 = df.loc[df.A > 5, :]


Frage 4
Ich versuche, Spalte "C" an Ort und Stelle von zu löschen

   A  B  C  D  E
1  9  3  5  2  4
2  7  6  8  8  1

Aber mit

df2.drop('C', axis=1, inplace=True)

Wirft SettingWithCopyWarning. Warum passiert dies?

Dies liegt daran, dass df2 Als Ansicht aus einem anderen Slicing-Vorgang erstellt wurde, z

df2 = df[df.A > 5]

Die Lösung besteht darin, entweder eine copy() von df zu erstellen oder loc wie zuvor zu verwenden.

100
cs95

Pandas Dataframe-Kopierwarnung

Wenn du etwas so machst:

quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]

pandas.ix in diesem Fall gibt einen neuen, eigenständigen Datenrahmen zurück.

Alle Werte, die Sie in diesem Datenrahmen ändern möchten, ändern den ursprünglichen Datenrahmen nicht.

Das ist es, worüber Pandas Sie warnen wollen.


Warum .ix eine schlechte Idee ist

Das .ix-Objekt versucht mehr als nur eine Sache zu tun, und für jeden, der etwas über sauberen Code gelesen hat, ist dies ein starker Geruch.

In Anbetracht dieses Datenrahmens:

df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})

Zwei Verhalten:

dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2

Verhalten eins: dfcopy ist jetzt ein eigenständiger Datenrahmen. Das Ändern ändert sich nicht df

df.ix[0, "a"] = 3

Verhalten zwei: Dies ändert den ursprünglichen Datenrahmen.


Verwenden Sie stattdessen .loc

Die Pandas-Entwickler erkannten, dass das .ix-Objekt [spekulativ] ziemlich übelriechend war, und erstellten zwei neue Objekte, die den Zugriff und die Zuordnung von Daten erleichtern. (Das andere ist .iloc)

.loc ist schneller, da nicht versucht wird, eine Kopie der Daten zu erstellen.

.loc dient dazu, den vorhandenen Datenrahmen an Ort und Stelle zu ändern, wodurch der Speicher effizienter wird.

.loc ist vorhersehbar, hat ein Verhalten.


Die Lösung

Was Sie in Ihrem Codebeispiel tun, ist das Laden einer großen Datei mit vielen Spalten, die dann kleiner wird.

Die pd.read_csv-Funktion kann Ihnen dabei helfen, das Laden der Datei zu beschleunigen.

Also, anstatt dies 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]]

Mach das

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

Dadurch werden nur die Spalten gelesen, für die Sie sich interessieren, und sie richtig benannt. Keine Notwendigkeit, das böse .ix-Objekt zu verwenden, um magisches Zeug zu machen.

34
firelynx

Um jeden Zweifel auszuräumen, bestand meine Lösung darin, eine tiefe Kopie des Slice anstelle einer regulären Kopie zu erstellen .. Dies kann je nach Kontext nicht zutreffend sein (Speicherbeschränkungen/Slice-Größe, potenzielle Leistungsbeeinträchtigung - besonders wenn Die Kopie erscheint in einer Schleife, wie sie es für mich getan hat, etc ...)

Um klar zu sein, hier ist die Warnung, die ich erhalten habe:

/opt/anaconda3/lib/python3.6/site-packages/ipykernel/__main__.py:54:
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

Illustration

Ich hatte Zweifel, dass die Warnung wegen einer Spalte ausgelöst wurde, die ich auf eine Kopie der Scheibe fallen ließ. Obwohl technisch nicht versucht wurde, einen Wert in der Kopie des Slice festzulegen, war dies immer noch eine Modifikation der Kopie des Slice. Im Folgenden sind die (vereinfachten) Schritte aufgeführt, die ich zur Bestätigung des Verdachts unternommen habe, aber ich hoffe, es hilft diejenigen von uns, die versuchen, die Warnung zu verstehen.

Beispiel 1: Das Ablegen einer Spalte auf dem Original wirkt sich auf die Kopie aus

Das wussten wir schon, aber das ist eine gesunde Erinnerung. Dies istNICHTworum es in der Warnung geht.

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123


>> df2 = df1
>> df2

A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 affects df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    B
0   121
1   122
2   123

Es ist möglich, Änderungen an df1 zu vermeiden, die sich auf df2 auswirken

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2
A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 does not affect df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    A   B
0   111 121
1   112 122
2   113 123

Beispiel 2: Wenn Sie eine Spalte auf der Kopie ablegen, kann dies Auswirkungen auf das Original haben

Dies veranschaulicht eigentlich die Warnung.

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> df2 = df1
>> df2

    A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df2 can affect df1
# No slice involved here, but I believe the principle remains the same?
# Let me know if not
>> df2.drop('A', axis=1, inplace=True)
>> df1

B
0   121
1   122
2   123

Es ist möglich, Änderungen an df2 zu vermeiden, die sich auf df1 auswirken

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2

A   B
0   111 121
1   112 122
2   113 123

>> df2.drop('A', axis=1, inplace=True)
>> df1

A   B
0   111 121
1   112 122
2   113 123

Prost!

6
Raphvanns

Wenn Sie das Slice einer Variablen zugewiesen haben und mit der Variablen wie folgt festlegen möchten:

df2 = df[df['A'] > 2]
df2['B'] = value

Und Sie möchten keine Jeffs-Lösung verwenden, weil Ihr Condition-Computing df2 zu lang ist oder aus anderen Gründen.

df.loc[df2.index.tolist(), 'B'] = value

df2.index.tolist() gibt die Indizes aus allen Einträgen in df2 zurück, die dann verwendet werden, um die Spalte B im ursprünglichen Datenrahmen festzulegen.

2
Steohan

Sie könnten das ganze Problem so vermeiden, glaube ich:

return (
    pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
    .rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
    .ix[:,[0,3,2,1,4,5,8,9,30,31]]
    .assign(
        TClose=lambda df: df['TPrice'],
        RT=lambda df: 100 * (df['TPrice']/quote_df['TPCLOSE'] - 1),
        TVol=lambda df: df['TVol']/TVOL_SCALE,
        TAmt=lambda df: df['TAmt']/TAMT_SCALE,
        STK_ID=lambda df: df['STK'].str.slice(13,19),
        STK_Name=lambda df: df['STK'].str.slice(21,30)#.decode('gb2312'),
        TDate=lambda df: df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10]),
    )
)

Assign verwenden Aus der documentation : 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 den Artikel von Tom Augspurger über Method Chaining in Pandas: https://tomaugspurger.github.io/method-chaining

1
hughdbrown

Für mich ist dieses Problem in einem folgenden> vereinfachten <Beispiel aufgetreten. Und ich konnte es auch lösen (hoffentlich mit einer korrekten Lösung):

alter Code mit Warnung:

def update_old_dataframe(old_dataframe, new_dataframe):
    for new_index, new_row in new_dataframe.iterrorws():
        old_dataframe.loc[new_index] = update_row(old_dataframe.loc[new_index], new_row)

def update_row(old_row, new_row):
    for field in [list_of_columns]:
        # line with warning because of chain indexing old_dataframe[new_index][field]
        old_row[field] = new_row[field]  
    return old_row

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 Series sind, habe ich die Zeile durch Folgendes ersetzt:

old_row.at[field] = new_row.at[field]

Methode für den Zugriff auf/Lookups für eine Series. Obwohl beide einwandfrei funktionieren und das Ergebnis gleich ist, muss ich die Warnungen auf diese Weise nicht deaktivieren (= für andere Probleme bei der Kettenindizierung an anderer Stelle aufbewahren).

Ich hoffe das kann jemandem helfen.

1
Petr Szturc

Das sollte funktionieren:

quote_df.loc[:,'TVol'] = quote_df['TVol']/TVOL_SCALE
1
jrouquie

Einige möchten möglicherweise einfach die Warnung unterdrücken:

class SupressSettingWithCopyWarning:
    def __enter__(self):
        pd.options.mode.chained_assignment = None

    def __exit__(self, *args):
        pd.options.mode.chained_assignment = 'warn'

with SupressSettingWithCopyWarning():
    #code that produces warning
0
delica

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 Datenrahmens könnte daher verwirrend sein.

Die einfache Lösung ist (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.

df  # Some DataFrame
df = df.loc[:, 0:2]  # Some filtering (unsure whether a view or copy is returned)
df = df.copy()  # Ensuring a copy is made
df[df["Name"] == "John"] = "Johny"  # Assignment can be done now (no warning)

0
Mikulas

Followup Anfängerfrage/Bemerkung

Vielleicht eine Klarstellung für andere Anfänger wie mich (ich komme aus R, was unter der Haube ein bisschen anders zu funktionieren scheint). Der folgende harmlos aussehende und funktionale Code erzeugte ständig die SettingWithCopy-Warnung, und ich konnte nicht herausfinden, warum. Ich hatte die mit "verketteter Indexierung" ausgegebene Ausgabe gelesen und verstanden, aber mein Code enthält keine:

def plot(pdb, df, title, **kw):
    df['target'] = (df['ogg'] + df['ugg']) / 2
    # ...

Aber später, viel zu spät, habe ich gesehen, wo die plot () - Funktion heißt:

    df = data[data['anz_emw'] > 0]
    pixbuf = plot(pdb, df, title)

"Df" ist also kein Datenrahmen, sondern ein Objekt, das sich irgendwie daran erinnert, dass es durch Indizieren eines Datenrahmens erstellt wurde (also eine Ansicht?), Wodurch die Linie in plot () dargestellt wird.

 df['target'] = ...

gleichwertig

 data[data['anz_emw'] > 0]['target'] = ...

das ist eine verkettete Indizierung. Habe ich das richtig verstanden?

Sowieso, 

def plot(pdb, df, title, **kw):
    df.loc[:,'target'] = (df['ogg'] + df['ugg']) / 2

behoben.

0
musbur

Hier beantworte ich die Frage direkt. Wie man damit umgeht?

Machen Sie nach dem Slice eine .copy()1.

Warten Sie, gibt ein Slice keine Kopie zurück? Immerhin versucht die Warnmeldung dies zu sagen? Lesen Sie die lange Antwort:

import pandas as pd
df = pd.DataFrame({'x':[1,2,3]})
df0 = df[df.x>2]

Dies gibt eine Warnung:

df0['foo'] = 'bar'

Das tut nicht:

df1 = df[df.x>2].copy()
df1['foo'] = 'bar'

Sowohl df0 als auch df1 sind DataFrame-Objekte, aber etwas anderes ist der Fall, bei dem Pandas die Warnung drucken können. Lass uns herausfinden, was es ist.

import inspect
test = df[df.x>2]
test_copy = df[df.x>2].copy()
inspect.getmembers(test)
inspect.getmembers(test_copy)

Wenn Sie das gewünschte Diff-Werkzeug verwenden, werden Sie feststellen, dass neben einigen Adressen die einzigen wesentlichen Unterschiede bestehen:

|          | test    | test_copy |
| _is_copy | weakref | None      |
| _is_view | True    | False     |

Die Methode, die entscheidet, ob eine Warnung ausgegeben werden soll, ist DataFrame._check_setitem_copy, die _is_copy überprüft.

Die Warnung schlägt vor, .loc zu verwenden, aber wenn Sie .loc in einem Frame mit _is_copy verwenden, wird dieselbe Warnung trotzdem angezeigt. Irreführend? Ja. Nervig? Sie wetten Hilfreich? Potentiell, wenn verkettete Zuweisung verwendet wird. Sie kann diesen Fall jedoch nicht einmal unterscheiden und druckt die Warnung wahllos aus.


1 Dadurch wird eine tiefe Kopie der Daten erstellt. Daher möchten Sie diese Methode wahrscheinlich nicht für große Datenrahmen oder wenn eine flache Kopie gewünscht wird. Warnung: Wenn Sie Python-Objekte im Datenrahmen haben, werden im Gegensatz zu copy.deepcopy nur die Referenzen kopiert. Mehr Infos im doc .

0
user443854