Rechercher l'index des éléments dans la série pandas

154

Je sais que c'est une question très basique, mais pour une raison quelconque, je ne trouve pas de réponse. Comment puis-je obtenir l'index de certains éléments d'une série dans les pandas python? (la première occurrence suffirait)

Ie, j'aimerais quelque chose comme:

import pandas as pd
myseries = pd.Series([1,4,0,7,5], index=[0,1,2,3,4])
print myseries.find(7) # should output 3

Certes, il est possible de définir une telle méthode avec une boucle:

def find(s, el):
    for i in s.index:
        if s[i] == el: 
            return i
    return None

print find(myseries, 7)

mais je suppose qu'il devrait y avoir un meilleur moyen. Y a-t-il?

sashkello
la source

Réponses:

199
>>> myseries[myseries == 7]
3    7
dtype: int64
>>> myseries[myseries == 7].index[0]
3

Bien que j'admette qu'il devrait y avoir une meilleure façon de le faire, mais cela évite au moins d'itérer et de faire une boucle à travers l'objet et de le déplacer au niveau C.

Viktor Kerkez
la source
12
Le problème ici est qu'il suppose que l'élément recherché est en fait dans la liste. C'est décevant que les pandas ne semblent pas avoir d'opération de recherche intégrée.
jxramos
7
Cette solution ne fonctionne que si votre série a un index entier séquentiel. Si votre index de série est par date / heure, cela ne fonctionne pas.
Andrew Medlin
43

Conversion en index, vous pouvez utiliser get_loc

In [1]: myseries = pd.Series([1,4,0,7,5], index=[0,1,2,3,4])

In [3]: Index(myseries).get_loc(7)
Out[3]: 3

In [4]: Index(myseries).get_loc(10)
KeyError: 10

Gestion des doublons

In [5]: Index([1,1,2,2,3,4]).get_loc(2)
Out[5]: slice(2, 4, None)

Renverra un tableau booléen si les retours non contigus

In [6]: Index([1,1,2,1,3,2,4]).get_loc(2)
Out[6]: array([False, False,  True, False, False,  True, False], dtype=bool)

Utilise une table de hachage en interne, si vite

In [7]: s = Series(randint(0,10,10000))

In [9]: %timeit s[s == 5]
1000 loops, best of 3: 203 µs per loop

In [12]: i = Index(s)

In [13]: %timeit i.get_loc(5)
1000 loops, best of 3: 226 µs per loop

Comme le souligne Viktor, la création d'un index entraîne une surcharge de création unique (elle est encourue lorsque vous faites réellement quelque chose avec l'index, par exemple le is_unique)

In [2]: s = Series(randint(0,10,10000))

In [3]: %timeit Index(s)
100000 loops, best of 3: 9.6 µs per loop

In [4]: %timeit Index(s).is_unique
10000 loops, best of 3: 140 µs per loop
Jeff
la source
1
@Jeff si vous avez un index plus intéressant, ce n'est pas si facile ... mais je suppose que vous pouvez le faires.index[_]
Andy Hayden
11
In [92]: (myseries==7).argmax()
Out[92]: 3

Cela fonctionne si vous savez que 7 est là à l'avance. Vous pouvez vérifier cela avec (myseries == 7) .any ()

Une autre approche (très similaire à la première réponse) qui tient également compte de plusieurs 7 (ou aucun) est

In [122]: myseries = pd.Series([1,7,0,7,5], index=['a','b','c','d','e'])
In [123]: list(myseries[myseries==7].index)
Out[123]: ['b', 'd']
Alon
la source
Le fait de savoir que 7 est un élément à l'avance est juste. Cependant, l'utilisation d'une anyvérification n'est pas idéale car une double itération est nécessaire. Il y a un chouette post-op qui dévoilera toutes les Falseconditions que vous pouvez voir ici .
jxramos
1
Attention, si aucun élément ne correspond à cette condition, argmaxil renverra toujours 0 (au lieu de l'erreur).
cs95
8

Je suis impressionné par toutes les réponses ici. Ce n'est pas une nouvelle réponse, juste une tentative de résumer les horaires de toutes ces méthodes. J'ai considéré le cas d'une série de 25 éléments et j'ai assumé le cas général où l'index pourrait contenir n'importe quelle valeur et que vous vouliez la valeur d'index correspondant à la valeur de recherche qui se trouve vers la fin de la série.

Voici les tests de vitesse sur un MacBook Pro 2013 en Python 3.7 avec Pandas version 0.25.3.

In [1]: import pandas as pd                                                

In [2]: import numpy as np                                                 

In [3]: data = [406400, 203200, 101600,  76100,  50800,  25400,  19050,  12700, 
   ...:          9500,   6700,   4750,   3350,   2360,   1700,   1180,    850, 
   ...:           600,    425,    300,    212,    150,    106,     75,     53, 
   ...:            38]                                                                               

In [4]: myseries = pd.Series(data, index=range(1,26))                                                

In [5]: myseries[21]                                                                                 
Out[5]: 150

In [7]: %timeit myseries[myseries == 150].index[0]                                                   
416 µs ± 5.05 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [8]: %timeit myseries[myseries == 150].first_valid_index()                                        
585 µs ± 32.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [9]: %timeit myseries.where(myseries == 150).first_valid_index()                                  
652 µs ± 23.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [10]: %timeit myseries.index[np.where(myseries == 150)[0][0]]                                     
195 µs ± 1.18 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [11]: %timeit pd.Series(myseries.index, index=myseries)[150]                 
178 µs ± 9.35 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [12]: %timeit myseries.index[pd.Index(myseries).get_loc(150)]                                    
77.4 µs ± 1.41 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [13]: %timeit myseries.index[list(myseries).index(150)]
12.7 µs ± 42.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [14]: %timeit myseries.index[myseries.tolist().index(150)]                   
9.46 µs ± 19.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

La réponse de @ Jeff semble être la plus rapide - bien qu'elle ne gère pas les doublons.

Correction : Désolé, j'en ai manqué une, la solution de @Alex Spangher utilisant la méthode d'index de liste est de loin la plus rapide.

Mettre à jour : Ajout de la réponse de @ EliadL.

J'espère que cela t'aides.

Étonnant qu'une opération aussi simple nécessite des solutions aussi compliquées et que beaucoup soient si lentes. Plus d'une demi-milliseconde dans certains cas pour trouver une valeur dans une série de 25.

Facture
la source
1
Merci. Mais ne devriez-vous pas mesurer après sa myindex création, car il ne doit être créé qu'une seule fois?
EliadL le
Vous pourriez faire valoir cela, mais cela dépend du nombre de recherches comme celle-ci nécessaires. Cela ne vaut la peine de créer la myindexsérie que si vous allez faire la recherche plusieurs fois. Pour ce test, j'ai supposé qu'il n'était nécessaire qu'une seule fois et que le temps d'exécution total était important.
Bill
1
Je viens de rencontrer le besoin de cela ce soir, et utiliser .get_lock () sur le même objet Index à travers plusieurs recherches semble être le plus rapide. Je pense qu'une amélioration de la réponse serait de fournir les horaires pour les deux: y compris la création de l'index, et un autre timing de la recherche uniquement après sa création.
Rick soutient Monica
Oui, bon point. @EliadL a également dit cela. Cela dépend du nombre d'applications que la série est statique. Si des valeurs de la série changent, vous devez reconstruire pd.Index(myseries). Pour être juste avec les autres méthodes, j'ai supposé que la série originale avait peut-être changé depuis la dernière recherche.
Projet de loi
5

Une autre façon de procéder, bien que tout aussi insatisfaisante, est:

s = pd.Series([1,3,0,7,5],index=[0,1,2,3,4])

list(s).index(7)

renvoie: 3

Sur les tests de temps en utilisant un ensemble de données actuel avec lequel je travaille (considérez-le comme aléatoire):

[64]:    %timeit pd.Index(article_reference_df.asset_id).get_loc('100000003003614')
10000 loops, best of 3: 60.1 µs per loop

In [66]: %timeit article_reference_df.asset_id[article_reference_df.asset_id == '100000003003614'].index[0]
1000 loops, best of 3: 255 µs per loop


In [65]: %timeit list(article_reference_df.asset_id).index('100000003003614')
100000 loops, best of 3: 14.5 µs per loop
Alex Spangher
la source
4

Si vous utilisez numpy, vous pouvez obtenir un tableau des indécies que votre valeur est trouvée:

import numpy as np
import pandas as pd
myseries = pd.Series([1,4,0,7,5], index=[0,1,2,3,4])
np.where(myseries == 7)

Cela retourne un tuple à un élément contenant un tableau des indécies où 7 est la valeur dans myseries:

(array([3], dtype=int64),)
Alex
la source
3

vous pouvez utiliser Series.idxmax ()

>>> import pandas as pd
>>> myseries = pd.Series([1,4,0,7,5], index=[0,1,2,3,4])
>>> myseries.idxmax()
3
>>> 
Raki Gade
la source
5
Cela semble renvoyer uniquement l'index où se trouve l'élément max, pas un élément spécifique index of certain elementcomme la question posée.
jxramos
1

Une autre façon de le faire qui n'a pas encore été mentionnée est la méthode tolist:

myseries.tolist().index(7)

doit renvoyer l'index correct, en supposant que la valeur existe dans la série.

rmutalik
la source
1
@Alex Spangher a suggéré quelque chose de similaire le 17 septembre 2014. Voir sa réponse. J'ai maintenant ajouté les deux versions aux résultats des tests.
Facture le
0

Souvent, votre valeur se produit à plusieurs indices:

>>> myseries = pd.Series([0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1])
>>> myseries.index[myseries == 1]
Int64Index([3, 4, 5, 6, 10, 11], dtype='int64')
Ulf Aslak
la source
0

C'est l'approche la plus native et la plus évolutive que j'ai pu trouver:

>>> myindex = pd.Series(myseries.index, index=myseries)

>>> myindex[7]
3

>>> myindex[[7, 5, 7]]
7    3
5    4
7    3
dtype: int64
EliadL
la source