Bedingte Pandas ersetzen

123

Ich habe einen DataFrame und möchte die Werte in einer bestimmten Spalte, die einen Wert überschreiten, durch Null ersetzen. Ich hatte gedacht, dies sei ein Weg, dies zu erreichen:

df[df.my_channel > 20000].my_channel = 0

Wenn ich den Kanal in einen neuen Datenrahmen kopiere, ist das ganz einfach:

df2 = df.my_channel 

df2[df2 > 20000] = 0

Dies macht genau das, was ich will, scheint aber nicht mit dem Kanal als Teil des ursprünglichen DataFrame zu funktionieren.

BMichell
quelle
Ich habe hier gefunden, wonach Sie gesucht haben .
Füße nass

Antworten:

181

.ixDer Indexer funktioniert für Pandas-Versionen vor 0.20.0 einwandfrei. Da Pandas 0.20.0 jedoch nicht verfügbar ist, ist der .ixIndexer veraltet . Sie sollten ihn daher vermeiden. Stattdessen können Sie .locoder ilocIndexer verwenden. Sie können dieses Problem lösen, indem Sie:

mask = df.my_channel > 20000
column_name = 'my_channel'
df.loc[mask, column_name] = 0

Oder in einer Zeile:

df.loc[df.my_channel > 20000, 'my_channel'] = 0

maskhilft Ihnen bei der Auswahl der Zeilen, in denen df.my_channel > 20000sich der Name 0 befindet True, und df.loc[mask, column_name] = 0setzt den Wert 0 auf die ausgewählten Zeilen, in denen maskin der Spalte der Name steht column_name.

Update: In diesem Fall sollten locSie verwenden iloc, da Sie bei Verwendung eine Meldung erhalten NotImplementedError, dass die iLocation-basierte boolesche Indizierung für einen Ganzzahltyp nicht verfügbar ist .

lmiguelvargasf
quelle
80

Versuchen

df.loc[df.my_channel > 20000, 'my_channel'] = 0

Hinweis: Seit v0.20.0 ix ist zugunsten von loc/ veraltetiloc .

Lowtech
quelle
8
Danke dir. Ich fand auch meine eigene Lösung, nämlich: df.my_channel [df.my_channel> 20000] = 0
BMichell
2
@BMichell Ich denke, Ihre Lösung könnte Ihnen in 0.13 Warnungen geben, hatte noch keine Chance, es zu versuchen
Lowtech
Ertragsfehler: /opt/anaconda3/envs/python35/lib/python3.5/site-packages/ipykernel_launcher.py:1: SettingWithCopyWarning: Ein Wert versucht, für eine Kopie eines Slice aus einem DataFrame festgelegt zu werden die Dokumentation: pandas.pydata.org/pandas-docs/stable/… "" "Einstiegspunkt für den Start eines IPython-Kernels.
Rutger Hofste
@ RutgerHofste danke für die Erwähnung, noch ein anderes Argument nie Python3 verwenden
Lowtech
34

np.where Funktion funktioniert wie folgt:

df['X'] = np.where(df['Y']>=50, 'yes', 'no')

In Ihrem Fall möchten Sie:

import numpy as np
df['my_channel'] = np.where(df.my_channel > 20000, 0, df.my_channel)
seeiespi
quelle
19

Der Grund, warum Ihr ursprünglicher Datenrahmen nicht aktualisiert wird, liegt darin, dass die verkettete Indizierung dazu führen kann, dass Sie eine Kopie anstelle einer Ansicht Ihres Datenrahmens ändern. Die Dokumente geben diesen Rat:

Beim Festlegen von Werten in einem Pandas-Objekt muss darauf geachtet werden, dass die sogenannte verkettete Indizierung vermieden wird.

Sie haben einige Alternativen: -

loc + Boolesche Indizierung

loc kann zum Festlegen von Werten verwendet werden und unterstützt Boolesche Masken:

df.loc[df['my_channel'] > 20000, 'my_channel'] = 0

mask + Boolesche Indizierung

Sie können Ihrer Serie zuordnen:

df['my_channel'] = df['my_channel'].mask(df['my_channel'] > 20000, 0)

Oder Sie können Ihre Serie an Ort und Stelle aktualisieren:

df['my_channel'].mask(df['my_channel'] > 20000, 0, inplace=True)

np.where + Boolesche Indizierung

Sie können NumPy verwenden, indem Sie Ihre Originalserie zuweisen, wenn Ihre Bedingung nicht erfüllt ist. Die ersten beiden Lösungen sind jedoch sauberer, da sie explizit nur die angegebenen Werte ändern.

df['my_channel'] = np.where(df['my_channel'] > 20000, 0, df['my_channel'])
jpp
quelle
0

Ich würde die lambdaFunktion auf Serieseinem DataFramesolchen verwenden:

f = lambda x: 0 if x>100 else 1
df['my_column'] = df['my_column'].map(f)

Ich behaupte nicht, dass dies ein effizienter Weg ist, aber es funktioniert gut.

Ozkan Serttas
quelle
3
Dies ist ineffizient und wird nicht empfohlen, da es sich um eine Python-Schleife in einer zeilenweisen Operation handelt.
jpp
Danke, ich denke wir können lochier wie verwenden df.loc[: , 'my_column'] = df['my_column'].map(f). Ich weiß nicht, ob es so schnell ist wie die, die Sie unten hinzugefügt haben.
Ozkan Serttas
2
Nein, immer noch langsam, da Sie immer noch zeilenweise und nicht spaltenweise arbeiten.
jpp
0

Versuche dies:

df.my_channel = df.my_channel.where(df.my_channel <= 20000, other= 0)

oder

df.my_channel = df.my_channel.mask(df.my_channel > 20000, other= 0)

R. Shams
quelle