web-dev-qa-db-de.com

pandas: Filterzeilen von DataFrame mit Operatorverkettung

Die meisten Operationen in pandas können mit der Operatorverkettung ausgeführt werden (groupby, aggregate, apply usw.), aber die einzige Möglichkeit, Zeilen zu filtern, ist die normale Indizierung von Klammern

df_filtered = df[df['column'] == value]

Dies ist unansehnlich, da ich einer Variablen df zuweisen muss, bevor ich nach ihren Werten filtern kann. Gibt es etwas Ähnliches wie das Folgende?

df_filtered = df.mask(lambda x: x['column'] == value)
276
duckworthd

Ich bin nicht ganz sicher, was Sie wollen, und Ihre letzte Codezeile hilft auch nicht, aber trotzdem:

Die "verkettete" Filterung erfolgt durch "Verketten" der Kriterien im Booleschen Index.

In [96]: df
Out[96]:
   A  B  C  D
a  1  4  9  1
b  4  5  0  2
c  5  5  1  0
d  1  3  9  6

In [99]: df[(df.A == 1) & (df.D == 6)]
Out[99]:
   A  B  C  D
d  1  3  9  6

Wenn Sie Methoden verketten möchten, können Sie Ihre eigene Maskenmethode hinzufügen und diese verwenden.

In [90]: def mask(df, key, value):
   ....:     return df[df[key] == value]
   ....:

In [92]: pandas.DataFrame.mask = mask

In [93]: df = pandas.DataFrame(np.random.randint(0, 10, (4,4)), index=list('abcd'), columns=list('ABCD'))

In [95]: df.ix['d','A'] = df.ix['a', 'A']

In [96]: df
Out[96]:
   A  B  C  D
a  1  4  9  1
b  4  5  0  2
c  5  5  1  0
d  1  3  9  6

In [97]: df.mask('A', 1)
Out[97]:
   A  B  C  D
a  1  4  9  1
d  1  3  9  6

In [98]: df.mask('A', 1).mask('D', 6)
Out[98]:
   A  B  C  D
d  1  3  9  6
341

Filter können mit einem Pandas Abfrage verkettet werden:

df = pd.DataFrame( np.random.randn(30,3), columns = ['a','b','c'])
df_filtered = df.query('a>0').query('0<b<2')

Filter können auch in einer einzigen Abfrage kombiniert werden:

df_filtered = df.query('a>0 and 0<b<2')
91
bscan

Die Antwort von @lodagro ist großartig. Ich würde es erweitern, indem ich die Maskenfunktion verallgemeinere als:

def mask(df, f):
  return df[f(df)]

Dann kannst du Sachen machen wie:

df.mask(lambda x: x[0] < 0).mask(lambda x: x[1] > 0)
62
Daniel Velkov

Seit Version 0.18.1 akzeptiert die .loc -Methode einen aufrufbaren Wert zur Auswahl. Zusammen mit Lambda-Funktionen können Sie sehr flexible verkettbare Filter erstellen:

import numpy as np
import pandas as pd

df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
df.loc[lambda df: df.A == 80]  # equivalent to df[df.A == 80] but chainable

df.sort_values('A').loc[lambda df: df.A > 80].loc[lambda df: df.B > df.A]

Wenn Sie nur filtern, können Sie auch den .loc weglassen.

21
Rafael Barbosa

Ich biete dies für zusätzliche Beispiele. Dies ist die gleiche Antwort wie https://stackoverflow.com/a/28159296/

Ich werde weitere Änderungen hinzufügen, um diesen Beitrag nützlicher zu machen.

pandas.DataFrame.query
query wurde genau zu diesem Zweck hergestellt. Betrachten Sie den Datenrahmen df

_import pandas as pd
import numpy as np

np.random.seed([3,1415])
df = pd.DataFrame(
    np.random.randint(10, size=(10, 5)),
    columns=list('ABCDE')
)

df

   A  B  C  D  E
0  0  2  7  3  8
1  7  0  6  8  6
2  0  2  0  4  9
3  7  3  2  4  3
4  3  6  7  7  4
5  5  3  7  5  9
6  8  7  6  4  7
7  6  2  6  6  5
8  2  8  7  5  8
9  4  7  6  1  5
_

Verwenden wir query, um alle Zeilen zu filtern, in denen _D > B_

_df.query('D > B')

   A  B  C  D  E
0  0  2  7  3  8
1  7  0  6  8  6
2  0  2  0  4  9
3  7  3  2  4  3
4  3  6  7  7  4
5  5  3  7  5  9
7  6  2  6  6  5
_

Was wir verketten

_df.query('D > B').query('C > B')
# equivalent to
# df.query('D > B and C > B')
# but defeats the purpose of demonstrating chaining

   A  B  C  D  E
0  0  2  7  3  8
1  7  0  6  8  6
4  3  6  7  7  4
5  5  3  7  5  9
7  6  2  6  6  5
_
15
piRSquared

Meine Antwort ist ähnlich wie die anderen. Wenn Sie keine neue Funktion erstellen möchten, können Sie das verwenden, was pandas bereits für Sie definiert hat. Verwenden Sie die Pipe-Methode.

df.pipe(lambda d: d[d['column'] == value])
7
Stewbaca

Ich hatte die gleiche Frage, außer dass ich die Kriterien in einer OR Bedingung kombinieren wollte. Das von Wouter Overmeire angegebene Format kombiniert die Kriterien in einer UND-Bedingung, sodass beide erfüllt sein müssen:

In [96]: df
Out[96]:
   A  B  C  D
a  1  4  9  1
b  4  5  0  2
c  5  5  1  0
d  1  3  9  6

In [99]: df[(df.A == 1) & (df.D == 6)]
Out[99]:
   A  B  C  D
d  1  3  9  6

Aber ich habe festgestellt, dass, wenn Sie jede Bedingung in (... == True) einschließen und die Kriterien mit einer Pipe verbinden, die Kriterien in einer OR-Bedingung kombiniert werden, die erfüllt ist, wenn eine der beiden Bedingungen erfüllt ist:

df[((df.A==1) == True) | ((df.D==6) == True)]
7
sharon

pandas bietet zwei Alternativen zur Antwort von Wouter Overmeire, die nicht außer Kraft gesetzt werden müssen. Einer ist .loc[.] mit einem Callable, wie in

df_filtered = df.loc[lambda x: x['column'] == value]

der andere ist .pipe(), wie in

df_filtered = df.pipe(lambda x: x['column'] == value)
5

Wenn Sie alle gängigen Booleschen Masken sowie eine Universalmaske anwenden möchten, können Sie Folgendes in eine Datei einfügen und diese dann einfach wie folgt zuweisen:

pd.DataFrame = apply_masks()

Verwendungszweck:

A = pd.DataFrame(np.random.randn(4, 4), columns=["A", "B", "C", "D"])
A.le_mask("A", 0.7).ge_mask("B", 0.2)... (May be repeated as necessary

Es ist ein bisschen kitschig, aber es kann die Dinge ein bisschen sauberer machen, wenn Sie kontinuierlich Datensätze nach Filtern hacken und ändern. Es gibt auch einen Universalfilter, der von Daniel Velkov oben in der Funktion gen_mask angepasst wurde und den Sie bei Bedarf mit Lambda-Funktionen oder auf andere Weise verwenden können.

Zu speichernde Datei (ich benutze masks.py):

import pandas as pd

def eq_mask(df, key, value):
    return df[df[key] == value]

def ge_mask(df, key, value):
    return df[df[key] >= value]

def gt_mask(df, key, value):
    return df[df[key] > value]

def le_mask(df, key, value):
    return df[df[key] <= value]

def lt_mask(df, key, value):
    return df[df[key] < value]

def ne_mask(df, key, value):
    return df[df[key] != value]

def gen_mask(df, f):
    return df[f(df)]

def apply_masks():

    pd.DataFrame.eq_mask = eq_mask
    pd.DataFrame.ge_mask = ge_mask
    pd.DataFrame.gt_mask = gt_mask
    pd.DataFrame.le_mask = le_mask
    pd.DataFrame.lt_mask = lt_mask
    pd.DataFrame.ne_mask = ne_mask
    pd.DataFrame.gen_mask = gen_mask

    return pd.DataFrame

if __== '__main__':
    pass
4
dantes_419

Diese Lösung ist hackiger in Bezug auf die Implementierung, aber ich finde sie viel sauberer in Bezug auf die Verwendung und sie ist mit Sicherheit allgemeiner als die anderen vorgeschlagenen.

https://github.com/toobaz/generic_utils/blob/master/generic_utils/pandas/where.py

Sie müssen nicht das gesamte Repo herunterladen: Speichern Sie die Datei und tun

from where import where as W

sollte ausreichen. Dann benutzt du es so:

df = pd.DataFrame([[1, 2, True],
                   [3, 4, False], 
                   [5, 7, True]],
                  index=range(3), columns=['a', 'b', 'c'])
# On specific column:
print(df.loc[W['a'] > 2])
print(df.loc[-W['a'] == W['b']])
print(df.loc[~W['c']])
# On entire - or subset of a - DataFrame:
print(df.loc[W.sum(axis=1) > 3])
print(df.loc[W[['a', 'b']].diff(axis=1)['b'] > 1])

Ein etwas weniger dummes Anwendungsbeispiel:

data = pd.read_csv('ugly_db.csv').loc[~(W == '$null$').any(axis=1)]

Übrigens: Auch in dem Fall, in dem Sie nur Boolesche Spalten verwenden,

df.loc[W['cond1']].loc[W['cond2']]

kann viel effizienter sein als

df.loc[W['cond1'] & W['cond2']]

weil es cond2 nur auswertet, wenn cond1True ist.

HAFTUNGSAUSSCHLUSS: Ich gab diese Antwort zuerst anderswo weil ich das nicht gesehen hatte.

3

Sie möchten lediglich eine Demonstration mit loc hinzufügen, um nicht nur nach Zeilen, sondern auch nach Spalten und einigen Vorteilen der verketteten Operation zu filtern.

Der folgende Code kann die Zeilen nach Wert filtern.

df_filtered = df.loc[df['column'] == value]

Wenn Sie es ein wenig ändern, können Sie auch die Spalten filtern.

df_filtered = df.loc[df['column'] == value, ['year', 'column']]

Warum wollen wir also eine verkettete Methode? Die Antwort ist, dass es einfach zu lesen ist, wenn Sie viele Operationen haben. Zum Beispiel,

res =  df\
    .loc[df['station']=='USA', ['TEMP', 'RF']]\
    .groupby('year')\
    .agg(np.nanmean)
2
Ken T

Dies ist unansehnlich, da ich einer Variablen df zuweisen muss, bevor ich nach ihren Werten filtern kann.

df[df["column_name"] != 5].groupby("other_column_name")

scheint zu funktionieren: Sie können den Operator [] auch verschachteln. Vielleicht haben sie es hinzugefügt, seit Sie die Frage gestellt haben.

2
serv-inc

Sie können auch die Bibliothek numpy für logische Operationen verwenden. Es ist ziemlich schnell.

df[np.logical_and(df['A'] == 1 ,df['B'] == 6)]
1
Akash Basudevan

Wenn Sie Ihre Spalten für die Suche als Indizes festlegen, können Sie mit DataFrame.xs() einen Querschnitt erstellen. Dies ist nicht so vielseitig wie die Antwort query, kann jedoch in bestimmten Situationen hilfreich sein.

import pandas as pd
import numpy as np

np.random.seed([3,1415])
df = pd.DataFrame(
    np.random.randint(3, size=(10, 5)),
    columns=list('ABCDE')
)

df
# Out[55]: 
#    A  B  C  D  E
# 0  0  2  2  2  2
# 1  1  1  2  0  2
# 2  0  2  0  0  2
# 3  0  2  2  0  1
# 4  0  1  1  2  0
# 5  0  0  0  1  2
# 6  1  0  1  1  1
# 7  0  0  2  0  2
# 8  2  2  2  2  2
# 9  1  2  0  2  1

df.set_index(['A', 'D']).xs([0, 2]).reset_index()
# Out[57]: 
#    A  D  B  C  E
# 0  0  2  2  2  2
# 1  0  2  1  1  0
1
naught101