Encontre o índice de elementos em pandas Series

154

Eu sei que esta é uma pergunta muito básica, mas por algum motivo não consigo encontrar uma resposta. Como posso obter o índice de determinado elemento de uma série em pandas python? (a primeira ocorrência seria suficiente)

Ou seja, eu gostaria de 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

Certamente, é possível definir esse método com um loop:

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

print find(myseries, 7)

mas suponho que deveria haver uma maneira melhor. Existe?

sashkello
fonte

Respostas:

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

Embora eu admita que deve haver uma maneira melhor de fazer isso, mas isso pelo menos evita a iteração e o loop do objeto e o move para o nível C.

Viktor Kerkez
fonte
12
O problema aqui é que ele assume que o elemento que está sendo pesquisado está realmente na lista. É um pandas chato que parece não ter uma operação de busca embutida.
Jxramos
7
Esta solução funciona apenas se sua série tiver um índice inteiro seqüencial. Se o índice da série for por data e hora, isso não funcionará.
Andrew Medlin
43

Convertendo para um índice, você pode 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

Manuseio duplicado

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

Retornará uma matriz booleana se retornos não contíguos

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 uma hashtable internamente, tão 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 Viktor aponta, há uma sobrecarga de criação única para a criação de um índice (isso ocorre quando você realmente faz algo com o índice, por exemplo, o 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
fonte
1
@ Jeff se você tiver um índice mais interessante que não é tão fácil ... mas eu acho que você pode apenas fazers.index[_]
Andy Hayden
11
In [92]: (myseries==7).argmax()
Out[92]: 3

Isso funciona se você souber que 7 existe com antecedência. Você pode verificar isso com (myseries == 7) .any ()

Outra abordagem (muito semelhante à primeira resposta) que também é responsável por vários 7's (ou nenhum) é

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
fonte
O ponto sobre conhecer 7 é um elemento adiantado. No entanto, o uso de uma anyverificação não é ideal, pois é necessária uma iteração dupla. Há uma verificação pós-operatória interessante que revelará todas as Falsecondições que você pode ver aqui .
jxramos
1
Cuidado, se nenhum elemento corresponder a essa condição, argmaxainda retornará 0 (em vez de errar).
cs95
8

Estou impressionado com todas as respostas aqui. Esta não é uma resposta nova, apenas uma tentativa de resumir os tempos de todos esses métodos. Eu considerei o caso de uma série com 25 elementos e assumi o caso geral em que o índice poderia conter quaisquer valores e você deseja que o valor do índice corresponda ao valor de pesquisa que está no final da série.

Aqui estão os testes de velocidade em um MacBook Pro 2013 em Python 3.7 com Pandas versão 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)

A resposta de @ Jeff parece ser a mais rápida - embora não lide com duplicatas.

Correção : desculpe, perdi uma, a solução do @Alex Spangher usando o método de índice de lista é de longe o mais rápido.

Atualização : adicionada a resposta de @ EliadL.

Espero que isto ajude.

É surpreendente que uma operação tão simples exija soluções complicadas e muitas sejam muito lentas. Mais de meio milissegundo, em alguns casos, para encontrar um valor em uma série de 25.

Conta
fonte
1
Obrigado. Mas você não deveria medir depois de myindex criado, pois ele precisa ser criado apenas uma vez?
EliadL
Você poderia argumentar isso, mas isso depende de quantas pesquisas são necessárias. Só vale a pena criar a myindexsérie se você for fazer a pesquisa várias vezes. Para este teste, presumi que era necessário apenas uma vez e que o tempo total de execução era importante.
Bill
1
Acabei de encontrar a necessidade disso hoje à noite, e usar .get_lock () no mesmo objeto Index em várias pesquisas parece que deve ser o mais rápido. Penso que uma melhoria na resposta seria fornecer os horários para ambos: incluindo a criação do Índice e outro horário apenas para a pesquisa depois de criada.
Rick apoia Monica
Sim, bom argumento. @EliadL também disse isso. Depende de quantas aplicações a série é estática. Se algum valor da série mudar, será necessário reconstruir pd.Index(myseries). Para ser justo com os outros métodos, presumi que a série original poderia ter sido alterada desde a última pesquisa.
Bill
5

Outra maneira de fazer isso, embora igualmente insatisfatória é:

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

list(s).index(7)

retornos: 3

Em testes de tempo usando um conjunto de dados atual com o qual estou trabalhando (considere aleatório):

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

Se você usar numpy, poderá obter uma matriz das indecies encontradas em seu 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)

Isso retorna uma tupla de um elemento que contém uma matriz de indecies, em que 7 é o valor em myseries:

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

você pode 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
fonte
5
Isso parece retornar apenas o índice onde o elemento max é encontrado, não específico index of certain elementcomo a pergunta.
Jxramos # 30/17
1

Outra maneira de fazer isso que ainda não foi mencionado é o método tolist:

myseries.tolist().index(7)

deve retornar o índice correto, assumindo que o valor exista na série.

rmutalik
fonte
1
@Alex Spangher sugeriu algo semelhante em 17 de setembro de 14. Veja a resposta dele. Agora adicionei ambas as versões aos resultados do teste.
Bill
0

Geralmente, seu valor ocorre em vários í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
fonte
0

Esta é a abordagem mais nativa e escalável que eu poderia encontrar:

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

>>> myindex[7]
3

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