Pandas So filtern Sie eine Serie


89

Ich habe eine Serie wie diese, nachdem ich groupby ('name') ausgeführt und die Funktion mean () für eine andere Spalte verwendet habe

name
383      3.000000
663      1.000000
726      1.000000
737      9.000000
833      8.166667

Könnte mir bitte jemand zeigen, wie man die Zeilen mit 1.000000 Mittelwerten herausfiltert? Vielen Dank und ich schätze Ihre Hilfe sehr.


Wie würden Sie eine Reihe nach einer bestimmten Bedingung filtern?

Antworten:


124
In [5]:

import pandas as pd

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

s = pd.Series(test)
s = s[s != 1]
s
Out[0]:
383    3.000000
737    9.000000
833    8.166667
dtype: float64

8
Ich bevorzuge die folgenden Antworten, da sie verkettet werden können (dh sie müssen nicht definiert sund dann zweimal im Ausdruck verwendet werden). Funktioniert allerdings nur ab Pandas 0.18.
IanS

Siehe auch Zeitvergleiche in der Antwort von piRSquared .
IanS

61

Ab der Pandas-Version 0.18+ kann das Filtern einer Serie auch wie folgt erfolgen

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

pd.Series(test).where(lambda x : x!=1).dropna()

Kasse: http://pandas.pydata.org/pandas-docs/version/0.18.1/whatsnew.html#method-chaininng-improvements


3
So viel schöner mit Methodenverkettung (und erinnert mich an Spark.)
Dylan Hogg

Richtig, aber Spark macht in diesem Fall etwas Intuitiveres: Es werden einfach Zeilen entfernt, die nicht mit dem Prädikat übereinstimmen. Dies bedeutet, dass der Teil ".dropna ()" nicht verwendet wird, der mir eindeutig überflüssig erschien, bis ich das Dokument gelesen habe.
Wurde davon

41

Wie DACW hervorhob , gibt es Verbesserungen bei der Verkettung von Methoden in Pandas 0.18.1 bei der Methoden, die genau das tun, wonach Sie suchen.

Anstatt zu verwenden .where, können Sie Ihre Funktion entweder an den .locIndexer oder an den Serienindexer übergeben []und den Aufruf von .dropna: vermeiden

test = pd.Series({
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
})

test.loc[lambda x : x!=1]

test[lambda x: x!=1]

Ein ähnliches Verhalten wird für die Klassen DataFrame und NDFrame unterstützt.


2
Dies ist meine Lieblingsantwort, und es scheint auch die schnellste zu sein, ohne zu numpy zu werden (siehe Zeitvergleiche).
IanS

21

Ein schneller Weg, dies zu tun, besteht darin numpy, die zugrunde liegenden Arrays zu rekonstruieren . Siehe Timings unten.

mask = s.values != 1
pd.Series(s.values[mask], s.index[mask])

0
383    3.000000
737    9.000000
833    8.166667
dtype: float64

naives Timing

Geben Sie hier die Bildbeschreibung ein


, Ich mag deine Methode, ich möchte wissen, was passiert, wenn ich mehrere Masken habe. Thx
Menglong Li

1
@MenglongLi kommt darauf an, du solltest eine Frage stellen. Höchstwahrscheinlich würden Sie sie mit & kombinieren. mask = mask1 & mask2
piRSquared

6

Eine andere Möglichkeit besteht darin, zuerst in einen DataFrame zu konvertieren und die Abfragemethode zu verwenden (vorausgesetzt, Sie haben numexpr installiert):

import pandas as pd

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

s = pd.Series(test)
s.to_frame(name='x').query("x != 1")

Ich denke nicht, dass es eine gute Idee ist, eine Bedingung als Zeichenfolge zu übergeben
SzymonPajzert

1
Dies fügt den gesamten Overhead eines Datenrahmens hinzu und wird sehr langsam sein.
fantastisch

5

Wenn Sie eine verkettete Operation mögen, können Sie auch folgende compressFunktionen verwenden:

test = pd.Series({
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
})

test.compress(lambda x: x != 1)

# 383    3.000000
# 737    9.000000
# 833    8.166667
# dtype: float64

1

In meinem Fall hatte ich eine Panda-Serie, in der die Werte Tupel von Zeichen sind :

Out[67]
0    (H, H, H, H)
1    (H, H, H, T)
2    (H, H, T, H)
3    (H, H, T, T)
4    (H, T, H, H)

Daher könnte ich die Indizierung verwenden, um die Serien zu filtern, aber um den Index zu erstellen, den ich brauchte apply. Mein Zustand ist "finde alle Tupel, die genau ein 'H' haben".

series_of_tuples[series_of_tuples.apply(lambda x: x.count('H')==1)]

Ich gebe zu, es ist nicht "verkettbar" ist (dh ich wiederhole esseries_of_tuples zweimal; Sie müssen alle temporären Reihen in einer Variablen speichern, damit Sie apply (...) darauf aufrufen können).

Es kann (nebenbei ) auch andere Methoden geben, die elementweise arbeiten können, um einen Booleschen Index zu erzeugen..apply(...)

Viele andere Antworten (einschließlich akzeptierter Antworten), die die verkettbaren Funktionen verwenden, wie:

  • .compress()
  • .where()
  • .loc[]
  • []

Diese akzeptieren Callables (Lambdas), die auf die Serie angewendet werden , nicht auf die einzelnen Werte in diesen Serien!

Daher verhielt sich meine Tupelreihe seltsam, als ich versuchte, meine obige Bedingung / callable / lambda mit einer der verkettbaren Funktionen zu verwenden, wie .loc[]:

series_of_tuples.loc[lambda x: x.count('H')==1]

Erzeugt den Fehler:

KeyError: 'Level H muss mit name (None) identisch sein'

Ich war sehr verwirrt, aber es scheint, die Series.count- series_of_tuples.count(...)Funktion zu verwenden , was ich nicht wollte.

Ich gebe zu, dass eine alternative Datenstruktur besser sein könnte:

  • Ein Datentyp der Kategorie?
  • Ein Datenrahmen (jedes Element des Tupels wird zu einer Spalte)
  • Eine Reihe von Zeichenfolgen (verketten Sie einfach die Tupel miteinander):

Dadurch wird eine Reihe von Zeichenfolgen erstellt (dh durch Verketten des Tupels; Zusammenfügen der Zeichen im Tupel zu einer einzelnen Zeichenfolge).

series_of_tuples.apply(''.join)

So kann ich dann die verkettbare verwendenSeries.str.count

series_of_tuples.apply(''.join).str.count('H')==1
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.