[FIXED] Effiziente Möglichkeit, mehrere Filter auf Pandas DataFrame oder Series anzuwenden

Ausgabe

Ich habe ein Szenario, in dem ein Benutzer mehrere Filter auf ein Pandas DataFrame- oder Series-Objekt anwenden möchte . Im Wesentlichen möchte ich eine Reihe von Filtern (Vergleichsoperationen) effizient miteinander verketten, die zur Laufzeit vom Benutzer angegeben werden.

  • Die Filter sollten additiv sein (d. h. jeder angewendete sollte die Ergebnisse eingrenzen).
  • Ich verwende derzeit reindex()(wie unten), aber dies erstellt jedes Mal ein neues Objekt und kopiert die zugrunde liegenden Daten (wenn ich die Dokumentation richtig verstehe). Ich möchte dieses unnötige Kopieren vermeiden, da es beim Filtern einer großen Serie oder eines großen Datenrahmens wirklich ineffizient ist.
  • Ich denke, dass die Verwendung von apply(), map(), oder etwas Ähnlichem besser sein könnte. Ich bin ziemlich neu bei Pandas, also versuche ich immer noch, meinen Kopf um alles zu wickeln.
  • Außerdem möchte ich dies erweitern, sodass das übergebene Wörterbuch die zu bearbeitenden Spalten enthalten und einen gesamten DataFrame basierend auf dem Eingabewörterbuch filtern kann. Ich gehe jedoch davon aus, dass alles, was für eine Serie funktioniert, problemlos auf einen DataFrame erweitert werden kann.

TL;DR

Ich möchte ein Wörterbuch der folgenden Form nehmen und jede Operation auf ein bestimmtes Series-Objekt anwenden und ein “gefiltertes” Series-Objekt zurückgeben.

relops = {'>=': [1], '<=': [1]}

Langes Beispiel

Ich beginne mit einem Beispiel dessen, was ich derzeit habe, und filtere nur ein einzelnes Series-Objekt. Unten ist die Funktion, die ich derzeit verwende:

   def apply_relops(series, relops):
        """
        Pass dictionary of relational operators to perform on given series object
        """
        for op, vals in relops.iteritems():
            op_func = ops[op]
            for val in vals:
                filtered = op_func(series, val)
                series = series.reindex(series[filtered])
        return series

Der Benutzer stellt ein Wörterbuch mit den Operationen bereit, die er ausführen möchte:

>>> df = pandas.DataFrame({'col1': [0, 1, 2], 'col2': [10, 11, 12]})
>>> print df
>>> print df
   col1  col2
0     0    10
1     1    11
2     2    12

>>> from operator import le, ge
>>> ops ={'>=': ge, '<=': le}
>>> apply_relops(df['col1'], {'>=': [1]})
col1
1       1
2       2
Name: col1
>>> apply_relops(df['col1'], relops = {'>=': [1], '<=': [1]})
col1
1       1
Name: col1

Auch hier besteht das „Problem“ bei meinem obigen Ansatz darin, dass meiner Meinung nach viele möglicherweise unnötige Daten für die Zwischenschritte kopiert werden.

Lösung

Pandas (und numpy) ermöglichen eine boolesche Indizierung , die viel effizienter ist:

In [11]: df.loc[df['col1'] >= 1, 'col1']
Out[11]: 
1    1
2    2
Name: col1

In [12]: df[df['col1'] >= 1]
Out[12]: 
   col1  col2
1     1    11
2     2    12

In [13]: df[(df['col1'] >= 1) & (df['col1'] <=1 )]
Out[13]: 
   col1  col2
1     1    11

Wenn Sie dafür Hilfsfunktionen schreiben möchten, sollten Sie Folgendes in Betracht ziehen:

In [14]: def b(x, col, op, n): 
             return op(x			
,n) In [15]: def f(x, *b): return x[(np.logical_and(*b))] In [16]: b1 = b(df, 'col1', ge, 1) In [17]: b2 = b(df, 'col1', le, 1) In [18]: f(df, b1, b2) Out[18]: col1 col2 1 1 11

Update: Pandas 0.13 hat eine Abfragemethode für diese Art von Anwendungsfällen, vorausgesetzt, Spaltennamen sind gültige Bezeichner, die folgenden Arbeiten (und können für große Frames effizienter sein, da sie numexpr hinter den Kulissen verwenden):

In [21]: df.query('col1 <= 1 & 1 <= col1')
Out[21]:
   col1  col2
1     1    11


Beantwortet von –
Andy Hayden


Antwort geprüft von –
Candace Johnson (FixError Volunteer)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like