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)
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
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')
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)
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.
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
_
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])
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)]
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)
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
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 cond1
True
ist.
HAFTUNGSAUSSCHLUSS: Ich gab diese Antwort zuerst anderswo weil ich das nicht gesehen hatte.
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)
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.
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)]
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