it-swarm.com.de

So löschen Sie Zeilen aus einem Pandas-DataFrame basierend auf einem Bedingungsausdruck

Ich habe einen Pandas-DataFrame, und ich möchte Zeilen daraus löschen, bei denen die Länge der Zeichenfolge in einer bestimmten Spalte größer als 2 ist. Ich weiß, dass ich df.dropna() verwenden kann, um Zeilen zu entfernen, die NaN enthalten, aber ich sehe nicht So entfernen Sie Zeilen basierend auf einem bedingten Ausdruck. 

Die Antwort auf diese Frage scheint sehr nahe an dem zu sein, was ich will - es scheint, als sollte ich so etwas tun können:

df[(len(df['column name']) < 2)]

aber ich bekomme nur den fehler:

KeyError: u'no item named False'

Kann mir jemand sagen, was ich falsch mache?

162
sjs

Wenn Sie len(df['column name']) ausführen, erhalten Sie nur eine Zahl, nämlich die Anzahl der Zeilen im DataFrame (d. H. Die Länge der Spalte selbst). Wenn Sie len auf jedes Element in der Spalte anwenden möchten, verwenden Sie df['column name'].map(len). Also versuche

df[df['column name'].map(len) < 2]
96
BrenBarn

Um den ursprünglichen Titel dieser Frage direkt zu beantworten: "Löschen von Zeilen aus einem pandas DataFrame basierend auf einem bedingten Ausdruck" (was meines Wissens nicht unbedingt das Problem des OP ist, aber anderen Benutzern helfen könnte, auf diese Frage zu stoßen ) Eine Möglichkeit, dies zu tun, besteht darin, die drop -Methode zu verwenden:

df = df.drop(some labels)

df = df.drop(df[<some boolean condition>].index)

Beispiel

So entfernen Sie alle Zeilen, in denen die Spalte "Punktzahl" <50 ist:

df = df.drop(df[df.score < 50].index)

In-Place-Version (wie in den Kommentaren angegeben)

df.drop(df[df.score < 50].index, inplace=True)

Mehrere Bedingungen

(siehe Boolean Indexing )

Die Operatoren sind: | Für or, & Für and und ~ Für not. Diese müssen mithilfe von Klammern gruppiert werden.

Entfernen aller Zeilen, in denen die Spalte "Bewertung" <50 und> 20 ist

df = df.drop(df[(df.score < 50) & (df.score > 20)].index)

609
User

Ich habe nach einer Lösung dafür gesucht und bin auf einen naheliegenden Ansatz gestoßen, den Datenrahmen einfach zu filtern und ihn so dem ursprünglichen Datenrahmen wieder zuzuordnen

df = df[df["score"] > 50]

Ich werde die generische Lösung von @ Users erweitern, um eine drop freie Alternative bereitzustellen. Dies ist für Leute, die hier basierend auf dem Titel der Frage gerichtet sind (nicht das Problem von OP).

Angenommen, Sie möchten alle Zeilen mit negativen Werten löschen. Eine Liner-Lösung ist:

df = df[(df > 0).all(axis=1)]

Schritt für Schritt Erklärung: -

Generieren wir einen zufälligen 5x5-Normalverteilungsdatenrahmen

np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,5), columns=list('ABCDE'))
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
1 -0.977278  0.950088 -0.151357 -0.103219  0.410599
2  0.144044  1.454274  0.761038  0.121675  0.443863
3  0.333674  1.494079 -0.205158  0.313068 -0.854096
4 -2.552990  0.653619  0.864436 -0.742165  2.269755

Lassen Sie die Bedingung Negative löschen. Ein boolescher df, der die Bedingung erfüllt: -

df > 0
      A     B      C      D      E
0   True  True   True   True   True
1  False  True  False  False   True
2   True  True   True   True   True
3   True  True  False   True  False
4  False  True   True  False   True

Eine boolesche Reihe für alle Zeilen, die die Bedingung erfüllen. Wenn ein Element in der Zeile die Bedingung nicht erfüllt, wird die Zeile als falsch markiert

(df > 0).all(axis=1)
0     True
1    False
2     True
3    False
4    False
dtype: bool

Zum Schluss werden die Zeilen aus dem Datenrahmen basierend auf der Bedingung herausgefiltert

df[(df > 0).all(axis=1)]
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
2  0.144044  1.454274  0.761038  0.121675  0.443863

Sie können es wieder df zuweisen, um löschen vs filtern wie oben beschrieben vorzugehen
df = df[(df > 0).all(axis=1)]

Dies kann leicht erweitert werden, um Zeilen herauszufiltern, die NaNs (nicht numerische Einträge) enthalten:
df = df[(~df.isnull()).all(axis=1)]

Dies kann auch für folgende Fälle vereinfacht werden: Löschen Sie alle Zeilen, in denen die Spalte E negativ ist

df = df[(df.E>0)]

Ich möchte mit einigen Profilstatistiken zum Schluss kommen, warum die drop -Lösung von @ Users langsamer ist als die auf rohen Spalten basierende Filtration:

%timeit df_new = df[(df.E>0)]
345 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit dft.drop(dft[dft.E < 0].index, inplace=True)
890 µs ± 94.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Eine Spalte ist im Grunde genommen ein Series, d. H. Ein NumPy -Array. Sie kann kostenlos indiziert werden. Für Leute, die daran interessiert sind, wie die zugrunde liegende Speicherorganisation die Ausführungsgeschwindigkeit beeinflusst, ist hier ein großartiges Link zur Beschleunigung von Pandas :

7
Zakir

In Pandas können Sie str.len mit Ihrer Grenze machen und das boolesche Ergebnis verwenden, um diese zu filtern. 

df[df['column name'].str.len().lt(2)]
3
Wen-Ben

Wenn Sie Zeilen mit Datenrahmen auf der Grundlage einer komplizierten Bedingung für den Spaltenwert löschen möchten, kann das Schreiben in der oben gezeigten Weise kompliziert sein. Ich habe die folgende einfachere Lösung, die immer funktioniert. Nehmen wir an, Sie möchten die Spalte mit 'header' löschen, also holen Sie diese Spalte zuerst in eine Liste.

text_data = df ['name']. tolist ()

wende jetzt eine Funktion auf jedes Element der Liste an und setze diese in eine Pandaserie:

text_length = pd.Series ([func (t) für t in text_data])

in meinem Fall habe ich nur versucht, die Anzahl der Token zu ermitteln:

text_length = pd.Series ([len (t.split ()) für t in text_data])

fügen Sie nun eine zusätzliche Spalte mit der obigen Serie im Datenrahmen hinzu:

df = df.assign (text_length = text_length .values)

jetzt können wir die Bedingung in der neuen Spalte anwenden, z.

df = df [df.text_length> 10]

Ein Tief-/Hochpassfilter für diesen Text lautet wie folgt:

def pass_filter(df, label, length, pass_type):

    text_data = df[label].tolist()

    text_length = pd.Series([len(t.split()) for t in text_data])

    df = df.assign(text_length = text_length .values)

    if pass_type == 'high':
        df = df[df.text_length  >  length]

    if pass_type == 'low':
        df = df[df.text_length  <  length]

    df = df.drop(columns=['text_length'])

    return df

1
jayanti prasad