Trova l'indice dell'elemento nella serie panda


154

So che questa è una domanda molto semplice, ma per qualche motivo non riesco a trovare una risposta. Come posso ottenere l'indice di un determinato elemento di una serie in python panda? (la prima occorrenza sarebbe sufficiente)

Cioè, vorrei qualcosa di simile:

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

Certamente, è possibile definire un tale metodo con un ciclo:

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

print find(myseries, 7)

ma suppongo che dovrebbe esserci un modo migliore. È lì?

Risposte:


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

Anche se ammetto che dovrebbe esserci un modo migliore per farlo, ma questo almeno evita di scorrere e scorrere l'oggetto e lo sposta al livello C.


12
Il problema qui è che si presume che l'elemento cercato sia effettivamente nell'elenco. È un peccato che i panda non sembrano avere un'operazione di ricerca integrata.
jxramos,

7
Questa soluzione funziona solo se la tua serie ha un indice intero sequenziale. Se l'indice della tua serie è datetime, questo non funziona.
Andrew Medlin,

43

Convertendo in un indice, è possibile utilizzare 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

Gestione duplicata

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

Restituirà un array booleano se restituisce non contigui

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)

Usa una hashtable internamente, così velocemente

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

Come sottolinea Viktor, c'è un sovraccarico di creazione una tantum per la creazione di un indice (si verifica quando fai effettivamente qualcosa con l'indice, ad esempio il 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

1
@Jeff se hai un indice più interessante non è così facile ... ma immagino che tu possa fare solos.index[_]
Andy Hayden

11
In [92]: (myseries==7).argmax()
Out[92]: 3

Funziona se sai che 7 è in anticipo. Puoi verificarlo con (myseries == 7) .any ()

Un altro approccio (molto simile alla prima risposta) che rappresenta anche i 7 multipli (o nessuno) è

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']

Il punto di conoscere 7 è un elemento in anticipo è giusto. Tuttavia, utilizzare un anycontrollo non è l'ideale poiché è necessaria una doppia iterazione. C'è un bel controllo post operatorio che svelerà tutte le Falsecondizioni che puoi vedere qui .
jxramos,

1
Attento, se nessun elemento soddisfa questa condizione, argmaxrestituirà comunque 0 (invece di errori).
cs95,

8

Sono impressionato con tutte le risposte qui. Questa non è una nuova risposta, solo un tentativo di riassumere i tempi di tutti questi metodi. Ho considerato il caso di una serie con 25 elementi e assunto il caso generale in cui l'indice potrebbe contenere qualsiasi valore e si desidera che il valore dell'indice corrisponda al valore di ricerca che è verso la fine della serie.

Ecco i test di velocità su un MacBook Pro 2013 in Python 3.7 con Pandas versione 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 risposta di Jeff sembra essere la più veloce, anche se non gestisce i duplicati.

Correzione : mi dispiace, ne ho perso uno, la soluzione di @Alex Spangher che utilizza il metodo dell'indice elenco è di gran lunga la più veloce.

Aggiornamento : aggiunta la risposta di @ EliadL.

Spero che questo ti aiuti.

Incredibile che un'operazione così semplice richieda soluzioni così contorte e molte sono così lente. Oltre mezzo millisecondo in alcuni casi per trovare un valore in una serie di 25.


1
Grazie. Ma non dovresti misurare dopo che myindex è stato creato, dal momento che deve essere creato solo una volta?
EliadL

Si potrebbe obiettare, ma dipende da quante ricerche come questa sono richieste. Vale la pena creare la myindexserie solo se hai intenzione di cercare più volte. Per questo test ho pensato che fosse necessario solo una volta e il tempo totale di esecuzione era importante.
Bill

1
Stasera mi sono imbattuto in questa necessità e l'utilizzo di .get_lock () sullo stesso oggetto Index su più ricerche sembra che dovrebbe essere il più veloce. Penso che un miglioramento della risposta sarebbe quello di fornire i tempi per entrambi: compresa la creazione dell'Indice, e un altro tempismo della sola ricerca dopo che è stata creata.
Rick supporta Monica

Sì, buon punto. @EliadL ha anche detto questo. Dipende da quante applicazioni la serie è statica. Se i valori nelle serie cambiano, è necessario ricostruire pd.Index(myseries). Per essere onesti con gli altri metodi, ho ipotizzato che la serie originale potesse essere cambiata dall'ultima ricerca.
Bill

5

Un altro modo per farlo, anche se altrettanto insoddisfacente è:

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

list(s).index(7)

ritorni: 3

Nei test a tempo usando un set di dati corrente con cui sto lavorando (consideralo casuale):

[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

4

Se usi numpy, puoi ottenere una matrice delle indecie in cui viene trovato il tuo valore:

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)

Ciò restituisce una tupla di un elemento contenente una matrice delle indecie in cui 7 è il valore in myseries:

(array([3], dtype=int64),)

3

puoi usare Series.idxmax ()

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

5
Questo sembra restituire solo l'indice in cui si trova l'elemento max, non uno specifico index of certain elementcome la domanda posta.
jxramos,

1

Un altro modo di farlo che non è stato ancora menzionato è il metodo tolista:

myseries.tolist().index(7)

dovrebbe restituire l'indice corretto, supponendo che il valore esista nella serie.


1
@Alex Spangher ha suggerito qualcosa di simile il 17 settembre 14. Vedi la sua risposta. Ho ora aggiunto entrambe le versioni ai risultati del test.
Bill

0

Spesso il tuo valore si verifica in più indici:

>>> 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')

0

Questo è l'approccio più nativo e scalabile che ho trovato:

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

>>> myindex[7]
3

>>> myindex[[7, 5, 7]]
7    3
5    4
7    3
dtype: int64
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.