La plupart des opérations dans pandas
peuvent être effectuées avec une chaîne d'opérateurs (groupby
name__, aggregate
name__, apply
name__, etc.), mais le seul moyen que j'ai trouvé de filtrer les lignes est de l'indexer normal.
df_filtered = df[df['column'] == value]
Ceci est peu attrayant car il faut que j'assigne df
à une variable avant de pouvoir filtrer ses valeurs. Y a-t-il quelque chose de plus semblable au suivant?
df_filtered = df.mask(lambda x: x['column'] == value)
Je ne suis pas tout à fait sûr de ce que vous voulez, et votre dernière ligne de code n'aide pas non plus, mais de toute façon:
Le filtrage "chaîné" est effectué en "chaînant" les critères de l'index booléen.
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
Si vous souhaitez chaîner des méthodes, vous pouvez ajouter votre propre méthode de masque et l'utiliser.
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
Les filtres peuvent être chaînés à l’aide d’un Pandas requête :
df = pd.DataFrame( np.random.randn(30,3), columns = ['a','b','c'])
df_filtered = df.query('a>0').query('0<b<2')
Les filtres peuvent également être combinés en une seule requête:
df_filtered = df.query('a>0 and 0<b<2')
La réponse de @Lodagro est excellente. Je l'étendrais en généralisant la fonction masque comme suit:
def mask(df, f):
return df[f(df)]
Ensuite, vous pouvez faire des choses comme:
df.mask(lambda x: x[0] < 0).mask(lambda x: x[1] > 0)
Depuis version 0.18.1 , la méthode .loc
accepte un appelable pour la sélection. Avec les fonctions lambda, vous pouvez créer des filtres chaînables très flexibles:
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]
Si vous ne faites que filtrer, vous pouvez également omettre le .loc
.
Je propose ceci pour des exemples supplémentaires. C'est la même réponse que https://stackoverflow.com/a/28159296/
Je vais ajouter d'autres modifications pour rendre ce post plus utile.
pandas.DataFrame.query
query
a été créé exactement à cette fin. Considérez la dataframe df
name__
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
Utilisons query
pour filtrer toutes les lignes où 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
Que nous enchaînons
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
Ma réponse est semblable aux autres. Si vous ne voulez pas créer de nouvelle fonction, vous pouvez utiliser ce que les pandas ont déjà défini pour vous. Utilisez la méthode du tuyau.
df.pipe(lambda d: d[d['column'] == value])
J'avais la même question, sauf que je voulais combiner les critères dans une condition OR. Le format donné par Wouter Overmeire combine les critères dans une condition AND de telle sorte que les deux doivent être satisfaits:
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
Mais j’ai trouvé que, si vous encapsulez chaque condition dans (... == True)
et joignez les critères à un tuyau, les critères sont combinés dans une condition OR, satisfaite chaque fois que l’une ou l’autre de ces conditions est vraie:
df[((df.A==1) == True) | ((df.D==6) == True)]
pandas propose deux alternatives à la réponse de Wouter Overmeire qui ne nécessitent aucune substitution. On est .loc[.]
avec un callable, comme dans
df_filtered = df.loc[lambda x: x['column'] == value]
l'autre est .pipe()
, comme dans
df_filtered = df.pipe(lambda x: x['column'] == value)
Si vous souhaitez appliquer tous les masques booléens courants ainsi qu'un masque à usage général, vous pouvez insérer les éléments suivants dans un fichier, puis les affecter comme suit:
pd.DataFrame = apply_masks()
Usage:
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
C'est un peu hacky mais cela peut rendre les choses un peu plus propres si vous coupez continuellement et changez les jeux de données en fonction des filtres. Il existe également un filtre polyvalent adapté de Daniel Velkov ci-dessus dans la fonction gen_mask que vous pouvez utiliser avec les fonctions lambda ou autrement si vous le souhaitez.
Fichier à sauvegarder (j'utilise 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
Cette solution est plus rigoureuse en termes de mise en œuvre, mais je la trouve beaucoup plus propre en termes d’utilisation, et elle est certainement plus générale que les autres proposées.
https://github.com/toobaz/generic_utils/blob/master/generic_utils/pandas/where.py
Vous n'avez pas besoin de télécharger le référentiel entier: enregistrer le fichier et le faire
from where import where as W
devrait suffire. Ensuite, vous l'utilisez comme ceci:
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])
Un exemple d'utilisation un peu moins stupide:
data = pd.read_csv('ugly_db.csv').loc[~(W == '$null$').any(axis=1)]
Soit dit en passant: même dans le cas où vous utilisez uniquement des cols booléens,
df.loc[W['cond1']].loc[W['cond2']]
peut être beaucoup plus efficace que
df.loc[W['cond1'] & W['cond2']]
car il évalue cond2
uniquement où cond1
est True
name__.
AVERTISSEMENT: J'ai d'abord donné cette réponse ailleurs parce que je n'avais pas vu cela.
Je veux juste ajouter une démonstration en utilisant loc
pour filtrer non seulement par lignes, mais également par colonnes et certains avantages de l'opération chaînée.
Le code ci-dessous peut filtrer les lignes par valeur.
df_filtered = df.loc[df['column'] == value]
En le modifiant un peu, vous pouvez également filtrer les colonnes.
df_filtered = df.loc[df['column'] == value, ['year', 'column']]
Alors, pourquoi voulons-nous une méthode chaînée? La réponse est que la lecture est simple si vous avez plusieurs opérations. Par exemple,
res = df\
.loc[df['station']=='USA', ['TEMP', 'RF']]\
.groupby('year')\
.agg(np.nanmean)
Ceci est peu attrayant car il faut que j'assigne
df
à une variable avant de pouvoir filtrer ses valeurs.
df[df["column_name"] != 5].groupby("other_column_name")
semble fonctionner: vous pouvez également imbriquer l'opérateur []
. Peut-être l'ont-ils ajouté depuis que vous avez posé la question.
Vous pouvez également utiliser la bibliothèque numpy pour les opérations logiques. C'est assez rapide.
df[np.logical_and(df['A'] == 1 ,df['B'] == 6)]
Si vous définissez vos colonnes pour une recherche en tant qu'index, vous pouvez utiliser DataFrame.xs()
pour effectuer une section transversale. Ce n’est pas aussi polyvalent que les réponses query
, mais cela peut être utile dans certaines situations.
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