Encontrar el índice del elemento en la serie pandas

154

Sé que esta es una pregunta muy básica, pero por alguna razón no puedo encontrar una respuesta. ¿Cómo puedo obtener el índice de cierto elemento de una serie en pandas python? (primera aparición sería suficiente)

Es decir, me gustaría algo como:

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

Ciertamente, es posible definir dicho método con un bucle:

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

print find(myseries, 7)

pero supongo que debería haber una mejor manera. ¿Esta ahí?

sashkello
fuente

Respuestas:

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

Aunque admito que debería haber una mejor manera de hacerlo, al menos esto evita iterar y recorrer el objeto y moverlo al nivel C.

Viktor Kerkez
fuente
12
El problema aquí es que supone que el elemento que se busca está realmente en la lista. Es un fastidio que los pandas no parecen tener una operación de búsqueda integrada.
jxramos
77
Esta solución solo funciona si su serie tiene un índice entero secuencial. Si el índice de su serie es por fecha y hora, esto no funciona.
Andrew Medlin
43

Convirtiendo a un índice, puede usar 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

Manejo duplicado

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

Devolverá una matriz booleana si no regresa contigua

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)

Utiliza una tabla hash internamente, tan rápido

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

Como señala Viktor, hay una sobrecarga de creación única para crear un índice (se incurre cuando realmente HACES algo con el índice, por ejemplo, el 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
fuente
1
@Jeff si tienes un índice más interesante, no es tan fácil ... pero supongo que puedes hacerlos.index[_]
Andy Hayden
11
In [92]: (myseries==7).argmax()
Out[92]: 3

Esto funciona si sabes que 7 está allí por adelantado. Puede verificar esto con (myseries == 7) .any ()

Otro enfoque (muy similar a la primera respuesta) que también representa múltiples 7 (o ninguno) es

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']
solo
fuente
El punto sobre saber 7 es un elemento por adelantado correcto. Sin embargo, usar un anycheque no es ideal ya que se necesita una doble iteración. Hay un chequeo post operatorio genial que revelará todas las Falsecondiciones que puedes ver aquí .
jxramos
1
Cuidado, si ningún elemento coincide con esta condición, argmaxaún devolverá 0 (en lugar de error).
cs95
8

Estoy impresionado con todas las respuestas aquí. Esta no es una respuesta nueva, solo un intento de resumir los tiempos de todos estos métodos. Consideré el caso de una serie con 25 elementos y asumí el caso general en el que el índice podría contener cualquier valor y desea el valor del índice correspondiente al valor de búsqueda que está hacia el final de la serie.

Aquí están las pruebas de velocidad en un MacBook Pro 2013 en Python 3.7 con Pandas versión 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 respuesta de @ Jeff parece ser la más rápida, aunque no maneja duplicados.

Corrección : Lo siento, me perdí una, la solución de @Alex Spangher usando el método de índice de lista es, con mucho, la más rápida.

Actualización : Se agregó la respuesta de @ EliadL.

Espero que esto ayude.

Es sorprendente que una operación tan simple requiera soluciones tan complicadas y muchas sean tan lentas. Más de medio milisegundo en algunos casos para encontrar un valor en una serie de 25.

Cuenta
fuente
1
Gracias. Pero, ¿no deberías medir después de myindex crearlo, ya que solo necesita crearse una vez?
EliadL
Podría argumentar eso, pero depende de cuántas búsquedas como esta se requieran. Solo vale la pena crear la myindexserie si vas a hacer la búsqueda muchas veces. Para esta prueba supuse que solo se necesitaba una vez y que el tiempo total de ejecución era importante.
Bill el
1
Me encontré con la necesidad de hacer esto esta noche, y usar .get_lock () en el mismo objeto Index en múltiples búsquedas parece que debería ser el más rápido. Creo que una mejora en la respuesta sería proporcionar los tiempos para ambos: incluida la creación del índice y otro momento de solo la búsqueda después de que se haya creado.
Rick apoya a Mónica
Si, buen punto. @EliadL también dijo eso. Depende de cuántas aplicaciones la serie sea estática. Si algún valor de la serie cambia, debe reconstruirlo pd.Index(myseries). Para ser justos con los otros métodos, supuse que la serie original podría haber cambiado desde la última búsqueda.
Bill
5

Otra forma de hacerlo, aunque igualmente insatisfactorio es:

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

list(s).index(7)

devuelve: 3

En pruebas de tiempo usando un conjunto de datos actual con el que estoy trabajando (considérelo al azar):

[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
fuente
4

Si usa numpy, puede obtener una matriz de las indecisiones en las que se encuentra su valor:

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)

Esto devuelve una tupla de un elemento que contiene una matriz de las indencias donde 7 es el valor en myseries:

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

puedes usar 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
fuente
55
Esto parece devolver solo el índice donde se encuentra el elemento max, no un específico index of certain elementcomo la pregunta formulada.
jxramos
1

Otra forma de hacerlo que aún no se ha mencionado es el método tolist:

myseries.tolist().index(7)

debería devolver el índice correcto, suponiendo que el valor exista en la Serie.

rmutalik
fuente
1
@Alex Spangher sugirió algo similar el 17 de septiembre de 2014. Ver su respuesta. Ahora he agregado ambas versiones a los resultados de la prueba.
Bill
0

A menudo, su valor se produce en múltiples índices:

>>> 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
fuente
0

Este es el enfoque más nativo y escalable que pude encontrar:

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

>>> myindex[7]
3

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