Ändern einer Teilmenge von Zeilen in einem Pandas-Datenrahmen

143

Angenommen, ich habe einen Pandas-DataFrame mit zwei Spalten, A und B. Ich möchte diesen DataFrame so ändern (oder eine Kopie erstellen), dass B immer NaN ist, wenn A 0 ist. Wie würde ich das erreichen?

Ich habe folgendes versucht

df['A'==0]['B'] = np.nan

und

df['A'==0]['B'].values.fill(np.nan)

ohne Erfolg.

Arthur B.
quelle
Wenn Sie nach einer sehr schnellen Lösung suchen, verwenden Sie NumPy's wherewie in dieser Lösung unten gezeigt
Ted Petrou

Antworten:

243

Verwendung .locfür die etikettenbasierte Indizierung:

df.loc[df.A==0, 'B'] = np.nan

Der df.A==0Ausdruck erstellt eine boolesche Reihe, die die Zeilen indiziert und 'B'die Spalte auswählt. Sie können dies auch verwenden, um eine Teilmenge einer Spalte zu transformieren, z.

df.loc[df.A==0, 'B'] = df.loc[df.A==0, 'B'] / 2

Ich weiß nicht genug über Pandas-Interna, um genau zu wissen, warum das funktioniert, aber das grundlegende Problem ist, dass die Indizierung in einen DataFrame manchmal eine Kopie des Ergebnisses und manchmal eine Ansicht des Originalobjekts zurückgibt. Laut Dokumentation hier hängt dieses Verhalten vom zugrunde liegenden Numpy-Verhalten ab. Ich habe festgestellt, dass der Zugriff auf alles in einem Vorgang (anstatt auf [eins] [zwei]) eher für die Einstellung funktioniert.

BrenBarn
quelle
Der zweite Teil davon ist eine nette Antwort auf eine Frage, die nicht einmal gestellt wurde ;-) Ich frage mich, ob dies immer noch die kanonische Pandas-Antwort ist, insbesondere b / c, es ist eine offensichtliche DRY-Verletzung, obwohl ich davon ausgehe, dass sie vorliegt Tatsache notwendig, um DRY angesichts der Einschränkungen von Pandas Interna zu verletzen? (Ich kann genau diese Art von Frage detaillierter posten, wollte aber sehen, ob Sie eine schnelle Antwort hatten, bevor ich dies tat)
JohnE
Wie kann ich einen Datenrahmen ohne Spaltennamen unterteilen, wie kann ich df nur nach Index unterteilen? df.loc [df [0] == 0] funktioniert nicht ... Was ist die Alternative? Vielen Dank
Amipro
89

Hier ist aus Pandas-Dokumenten zur erweiterten Indizierung:

In diesem Abschnitt erfahren Sie genau, was Sie brauchen! Es stellt sich heraus df.loc(wie .ix veraltet ist - wie viele unten ausgeführt haben), kann zum kühlen Schneiden / Würfeln eines Datenrahmens verwendet werden. Und. Es kann auch verwendet werden, um Dinge einzustellen.

df.loc[selection criteria, columns I want] = value

Brens Antwort lautet also: "Finde alle Orte, an denen du eine df.A == 0Spalte auswählst Bund auf np.nan". "

Badgley
quelle
2
Du hast meinen Tag gerettet. Klare Erklärung.
TwinPenguins
1
Ja, irgendwie loc[selection criteria, columns I want]bleibt es perfekt in deinem Kopf ...
EmEs
29

Ab Pandas ist 0.20 ix veraltet . Der richtige Weg ist, df.loc zu verwenden

Hier ist ein funktionierendes Beispiel

>>> import pandas as pd 
>>> import numpy as np 
>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN
>>> 

Erläuterung:

Wie im Dokument hier erläutert , .loc basiert es hauptsächlich auf Beschriftungen, kann aber auch mit einem booleschen Array verwendet werden .

Also, was wir oben tun, ist sich zu bewerben df.loc[row_index, column_index]durch:

  • Ausnutzung der Tatsache, dass locein boolesches Array als Maske verwendet werden kann, die Pandas mitteilt, in welcher Teilmenge von Zeilen wir ändern möchtenrow_index
  • Die Tatsache auszunutzen locist auch beschriftungsbasiert, um die Spalte unter Verwendung der Beschriftung 'B'in der auszuwählencolumn_index

Wir können logische, bedingte oder beliebige Operationen verwenden, die eine Reihe von Booleschen Werten zurückgeben, um das Array von Booleschen Werten zu erstellen. Im obigen Beispiel möchten wir alle rows, die ein enthalten 0, für das wir verwenden können df.A == 0, wie Sie im folgenden Beispiel sehen können. Dies gibt eine Reihe von Booleschen Werten zurück.

>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df 
   A  B
0  0  2
1  1  0
2  0  5
>>> df.A == 0 
0     True
1    False
2     True
Name: A, dtype: bool
>>> 

Dann verwenden wir das obige Array von Booleschen Werten, um die erforderlichen Zeilen auszuwählen und zu ändern:

>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN

Weitere Informationen finden Sie in der Dokumentation zur erweiterten Indizierung hier .

Mohamed Ali JAMAOUI
quelle
11

Verwenden Sie für eine massive Geschwindigkeitssteigerung die where-Funktion von NumPy.

Konfiguration

Erstellen Sie einen zweispaltigen DataFrame mit 100.000 Zeilen mit einigen Nullen.

df = pd.DataFrame(np.random.randint(0,3, (100000,2)), columns=list('ab'))

Schnelle Lösung mit numpy.where

df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)

Timings

%timeit df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)
685 µs ± 6.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.loc[df['a'] == 0, 'b'] = np.nan
3.11 ms ± 17.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy's whereist ungefähr 4x schneller

Ted Petrou
quelle
Ich war neugierig darauf, also habe ich es selbst getestet und der Unterschied war mit anderen Parametern noch größer. Numpy war fast zehnmal schneller beim Ersetzen von Nullen durch eine Ganzzahl anstelle von np.nan. Ich frage mich, was die zusätzliche Zeit kostet.
Alexander
Ist es notwendig , zu verwenden , .valuesin np.where(df.a.values == 0, np.nan, df.b.values)? Sieht aus wie np.where(df.a == 0, np.nan, df.b)funktioniert auch?
hsl
4

Um mehrere Spalten zu ersetzen, konvertieren Sie sie in ein numpy-Array mit .values:

df.loc[df.A==0, ['B', 'C']] = df.loc[df.A==0, ['B', 'C']].values / 2
Adrien Renaud
quelle