J'essaie de filtrer une trame de données de pandas en utilisant des seuils pour trois colonnes
import pandas as pd
df = pd.DataFrame({"A" : [6, 2, 10, -5, 3],
"B" : [2, 5, 3, 2, 6],
"C" : [-5, 2, 1, 8, 2]})
df = df.loc[(df.A > 0) & (df.B > 2) & (df.C > -1)].reset_index(drop = True)
df
A B C
0 2 5 2
1 10 3 1
2 3 6 2
Cependant, je veux le faire dans une fonction où les noms des colonnes et leurs seuils me sont donnés dans un dictionnaire. Voici mon premier essai qui fonctionne bien. Essentiellement, je mets le filtre dans la variable cond
et le lance simplement:
df = pd.DataFrame({"A" : [6, 2, 10, -5, 3],
"B" : [2, 5, 3, 2, 6],
"C" : [-5, 2, 1, 8, 2]})
limits_dic = {"A" : 0, "B" : 2, "C" : -1}
cond = "df = df.loc["
for key in limits_dic.keys():
cond += "(df." + key + " > " + str(limits_dic[key])+ ") & "
cond = cond[:-2] + "].reset_index(drop = True)"
exec(cond)
df
A B C
0 2 5 2
1 10 3 1
2 3 6 2
Maintenant, finalement, je mets tout dans une fonction et elle cesse de fonctionner (peut-être que la fonction exec
n'aime pas être utilisée dans une fonction!):
df = pd.DataFrame({"A" : [6, 2, 10, -5, 3],
"B" : [2, 5, 3, 2, 6],
"C" : [-5, 2, 1, 8, 2]})
limits_dic = {"A" : 0, "B" : 2, "C" : -1}
def filtering(df, limits_dic):
cond = "df = df.loc["
for key in limits_dic.keys():
cond += "(df." + key + " > " + str(limits_dic[key])+ ") & "
cond = cond[:-2] + "].reset_index(drop = True)"
exec(cond)
return(df)
df = filtering(df, limits_dic)
df
A B C
0 6 2 -5
1 2 5 2
2 10 3 1
3 -5 2 8
4 3 6 2
Je sais que la fonction exec
agit différemment dans une fonction, mais je ne savais pas comment résoudre le problème. De plus, je me demande s'il doit exister un moyen plus élégant de définir une fonction pour effectuer le filtrage à partir de deux entrées: 1) df
et 2) limits_dic = {"A" : 0, "B" : 2, "C" : -1}
. J'apprécierais vos pensées à ce sujet.
Si vous essayez de créer une requête dynamique, il existe des moyens plus simples. En voici une qui utilise une compréhension de liste et str.join
:
query = ' & '.join(['{}>{}'.format(k, v) for k, v in limits_dic.items()])
Ou, en utilisant f
- strings avec python-3.6 +,
query = ' & '.join([f'{k}>{v}' for k, v in limits_dic.items()])
print(query)
'A>0 & C>-1 & B>2'
Passez la chaîne de requête à df.query
, elle est conçue pour cela:
out = df.query(query)
print(out)
A B C
1 2 5 2
2 10 3 1
4 3 6 2
Vous pouvez également utiliser df.eval
si vous souhaitez obtenir un masque booléen pour votre requête, puis l'indexation devient simple:
mask = df.eval(query)
print(mask)
0 False
1 True
2 True
3 False
4 True
dtype: bool
out = df[mask]
print(out)
A B C
1 2 5 2
2 10 3 1
4 3 6 2
Si vous devez interroger des colonnes qui utilisent des données de chaîne, le code ci-dessus nécessitera une légère modification.
Considérons (données de cette réponse ):
df = pd.DataFrame({'gender':list('MMMFFF'),
'height':[4,5,4,5,5,4],
'age':[70,80,90,40,2,3]})
print (df)
gender height age
0 M 4 70
1 M 5 80
2 M 4 90
3 F 5 40
4 F 5 2
5 F 4 3
Et une liste de colonnes, d'opérateurs et de valeurs:
column = ['height', 'age', 'gender']
equal = ['>', '>', '==']
condition = [1.68, 20, 'F']
La modification appropriée est la suivante:
query = ' & '.join(f'{i} {j} {repr(k)}' for i, j, k in Zip(column, equal, condition))
df.query(query)
age gender height
3 40 F 5
Pour plus d'informations sur la famille de fonctions pd.eval()
, leurs fonctionnalités et leurs cas d'utilisation, veuillez consulter Évaluation de l'expression dynamique dans les pandas à l'aide de pd.eval () .
Une alternative à la version de @coldspeed:
conditions = None
for key, val in limit_dic.items():
cond = df[key] > val
if conditions is None:
conditions = cond
else:
conditions = conditions & cond
print(df[conditions])