Pandas Comment filtrer une série


94

J'ai une série comme celle-ci après avoir fait groupby ('name') et utilisé la fonction mean () sur une autre colonne

name
383      3.000000
663      1.000000
726      1.000000
737      9.000000
833      8.166667

Quelqu'un pourrait-il s'il vous plaît me montrer comment filtrer les lignes avec des valeurs moyennes de 1,000000? Merci et j'apprécie grandement votre aide.


Eh bien, comment filtreriez-vous une série sur une condition donnée?

Réponses:


127
In [5]:

import pandas as pd

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

s = pd.Series(test)
s = s[s != 1]
s
Out[0]:
383    3.000000
737    9.000000
833    8.166667
dtype: float64

10
Je préfère les réponses ci-dessous car elles peuvent être enchaînées (c'est-à-dire pas besoin de définir spuis de l'utiliser deux fois dans l'expression). Cela ne fonctionne qu'à partir de pandas 0.18.
IanS

Voir également les comparaisons de temps dans la réponse de piRSquared .
IanS

63

À partir de la version 0.18+ de pandas, le filtrage d'une série peut également être effectué comme ci-dessous

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

pd.Series(test).where(lambda x : x!=1).dropna()

Paiement: http://pandas.pydata.org/pandas-docs/version/0.18.1/whatsnew.html#method-chaininng-improvements


3
Tellement mieux avec le chaînage de méthodes (et cela me rappelle Spark.)
Dylan Hogg

C'est vrai mais Spark fait quelque chose de plus intuitif dans ce cas: il se débarrasse simplement des lignes qui ne correspondent pas au prédicat, c'est-à-dire ne pas utiliser la partie ".dropna ()" qui me semblait clairement superflue jusqu'à ce que je lis la doc. J'ai été mordu par ça: D
Florent Moiny

44

Comme l'a souligné DACW , il y a des améliorations de l'enchaînement des méthodes dans pandas 0.18.1 qui font très bien ce que vous recherchez.

Plutôt que d'utiliser .where, vous pouvez transmettre votre fonction à l' .locindexeur ou à l'indexeur de séries []et éviter l'appel à .dropna:

test = pd.Series({
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
})

test.loc[lambda x : x!=1]

test[lambda x: x!=1]

Un comportement similaire est pris en charge sur les classes DataFrame et NDFrame.


2
C'est ma réponse préférée, et elle semble également être la plus rapide sans descendre dans le numpy (voir les comparaisons de temps).
IanS

21

Un moyen rapide de procéder consiste à reconstruire en utilisant numpypour découper les tableaux sous-jacents. Voir les horaires ci-dessous.

mask = s.values != 1
pd.Series(s.values[mask], s.index[mask])

0
383    3.000000
737    9.000000
833    8.166667
dtype: float64

timing naïf

entrez la description de l'image ici


, J'aime ta méthode, je veux savoir si j'ai des multi-masques. Thx
Menglong Li

1
@MenglongLi dépend, vous devriez poser une question. Très probablement, vous les combinez avec &. mask = mask1 & mask2
piRSquared

6

Une autre méthode consiste à d'abord convertir en DataFrame et à utiliser la méthode de requête (en supposant que numexpr est installé):

import pandas as pd

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

s = pd.Series(test)
s.to_frame(name='x').query("x != 1")

Je ne pense pas que ce soit une bonne idée de passer une condition comme chaîne
SzymonPajzert

1
Cela ajoute toute la surcharge d'une dataframe et va être très lent.
fantastique

5

Si vous aimez une opération chaînée, vous pouvez également utiliser la compressfonction:

test = pd.Series({
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
})

test.compress(lambda x: x != 1)

# 383    3.000000
# 737    9.000000
# 833    8.166667
# dtype: float64

1

Dans mon cas, j'avais une série de panda où les valeurs sont des tuples de caractères :

Out[67]
0    (H, H, H, H)
1    (H, H, H, T)
2    (H, H, T, H)
3    (H, H, T, T)
4    (H, T, H, H)

Par conséquent, je pourrais utiliser l'indexation pour filtrer les séries, mais pour créer l'index dont j'avais besoin apply. Ma condition est "trouver tous les tuples qui ont exactement un" H "".

series_of_tuples[series_of_tuples.apply(lambda x: x.count('H')==1)]

J'avoue que ce n'est pas "chaînable" , (c'est-à-dire que je le répète series_of_tuplesdeux fois; vous devez stocker toute série temporaire dans une variable pour pouvoir appeler apply (...) dessus).

Il peut également y avoir d' autres méthodes (en plus .apply(...)) qui peuvent fonctionner élément par élément pour produire un index booléen.

Beaucoup d'autres réponses (y compris la réponse acceptée) utilisant les fonctions chaînables comme:

  • .compress()
  • .where()
  • .loc[]
  • []

Ceux-ci acceptent les callables (lambdas) qui sont appliqués à la série , pas aux valeurs individuelles de ces séries!

Par conséquent, ma série de tuples s'est comportée étrangement lorsque j'ai essayé d'utiliser ma condition ci-dessus / callable / lambda, avec l'une des fonctions chaînables, comme .loc[]:

series_of_tuples.loc[lambda x: x.count('H')==1]

Produit l'erreur:

KeyError: 'Le niveau H doit être identique au nom (aucun)'

J'étais très confus, mais il semble utiliser la fonction Series.countseries_of_tuples.count(...) , ce qui n'est pas ce que je voulais.

J'admets qu'une structure de données alternative peut être meilleure:

  • Un type de données Catégorie?
  • Un Dataframe (chaque élément du tuple devient une colonne)
  • Une série de chaînes (il suffit de concaténer les tuples ensemble):

Cela crée une série de chaînes (c'est-à-dire en concaténant le tuple; en joignant les caractères du tuple sur une seule chaîne)

series_of_tuples.apply(''.join)

Je peux donc utiliser le chaînableSeries.str.count

series_of_tuples.apply(''.join).str.count('H')==1
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.