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.


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 .


80

Versuchen

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

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


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)

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'])

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.


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)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.