2
votes

Pandas DataFrame sélectionne des lignes en fonction des valeurs de plusieurs colonnes dont les noms sont spécifiés dans une liste

J'ai le dataframe suivant:

    z   x   u   y
0   0   1   0   0
0   0   1   0   0
1   0   1   1   1
1   0   1   1   1
2   1   1   1   1
3   0   0   1   1
4   0   0   1   1
5   0   0   0   0
7   0   1   1   1
7   0   1   1   1
8   1   1   0   0
9   0   1   1   1
9   0   1   1   1
zs = ['z','x']
tf = ds[ds[zs].values == (0,1)]
tf

Comment sélectionner des lignes qui ont les valeurs (0,1) pour les noms de variables spécifiés dans une liste?

Voici ce que j'ai jusqu'à présent:

    z   x   u   y
0   0   1   0   0
1   0   1   1   1
2   1   1   1   1
3   0   0   1   1
4   0   0   1   1
5   0   0   0   0
6   1   0   1   1
7   0   1   1   1
8   1   1   0   0
9   0   1   1   1

Maintenant qui imprime:

import pandas as pd
import numpy as np
ds = pd.DataFrame({'z':np.random.binomial(n=1,p=0.5,size=10), 
                   'x':np.random.binomial(n=1,p=0.5,size=10), 
                   'u':np.random.binomial(n=1,p=0.5,size=10), 
                   'y':np.random.binomial(n=1,p=0.5,size=10)})
ds

Qui montre les doublons et a également une ligne incorrecte (ligne # 2 - 1,1,1,1). Des pensées ou des idées? Bien sûr, je suppose qu'il existe une manière pythonique de faire cela sans boucles imbriquées et sans forcer brutalement.


0 commentaires

4 Réponses :


2
votes

Vous pouvez faire:

cols = ['u','x']
bools = ds[cols].apply(lambda x: all(x == (0,1)), axis=1)
ds[bools]

   u  x  y  z
0  0  1  1  1
7  0  1  0  1
8  0  1  1  0


0 commentaires

4
votes

Vous pouvez utiliser la comparaison numpy diffusée:

querystr = ' and '.join([f'{c} == {v!r}' for c,  v in zip(cols, vals)])
df.query(querystr)

   z  x  u  y
0  0  1  0  0
1  0  1  1  1
7  0  1  1  1
9  0  1  1  1

Vous pouvez également utiliser np.logical_and.reduce:

cols = ['z', 'x']
vals = [0, 1]

df[np.logical_and.reduce([df[c] == v for c, v in zip(cols, vals)])]

   z  x  u  y
0  0  1  0  0
1  0  1  1  1
7  0  1  1  1
9  0  1  1  1


2 commentaires

Merci @coldspeed. C'est très utile. Savez-vous pourquoi ma requête simpliste donne la mauvaise réponse (doublons et ligne erronée)?


@vsm Vous étiez proche. Vous avez besoin d'un masque 1D pour indexer df . Ainsi, avec votre solution, vous auriez dû faire (df [['z', 'x']]. Values ​​== (0, 1)). All (axis = 1) pour voir lequel row satisfait à cette condition pour toutes les colonnes. C'est pourquoi c'était ma première option: vous montrer votre code fixe.



1
votes

En utilisant eq , et très similaire à la méthode numpy de Cold

df[df[zs].eq(pd.Series([0,1],index=zs),1).all(1)]
   z  x  u  y
0  0  1  0  0
1  0  1  1  1
7  0  1  1  1
9  0  1  1  1


0 commentaires

0
votes

Un moyen plus simple consiste à utiliser l ' indexation booléenne :

f = ds['z'] == 0
g = ds['x'] == 1
ds[f & g]


1 commentaires

"simple" mais ne s'adapte pas à plusieurs colonnes et valeurs. Voir np.logical_and.reduce pour savoir comment le généraliser (voir aussi la deuxième option de ma réponse).