Znajdź indeks elementu w serii pandy

154

Wiem, że to bardzo podstawowe pytanie, ale z jakiegoś powodu nie mogę znaleźć odpowiedzi. Jak mogę uzyskać indeks określonego elementu serii w pandach Pythona? (wystarczyłoby pierwsze wystąpienie)

To znaczy, chciałbym coś takiego:

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

Z pewnością można zdefiniować taką metodę za pomocą pętli:

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

print find(myseries, 7)

ale zakładam, że powinien być lepszy sposób. Jest tu?

sashkello
źródło

Odpowiedzi:

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

Chociaż przyznaję, że powinien być lepszy sposób na zrobienie tego, ale przynajmniej pozwala to uniknąć iteracji i zapętlenia obiektu i przenosi go na poziom C.

Viktor Kerkez
źródło
12
Problem polega na tym, że zakłada się, że szukany element znajduje się na liście. Wydaje się, że pandy nie mają wbudowanej operacji wyszukiwania.
jxramos
7
To rozwiązanie działa tylko wtedy, gdy seria ma sekwencyjny indeks całkowity. Jeśli indeks serii jest zgodny z datą i godziną, to nie działa.
Andrew Medlin,
43

Konwertując na indeks, możesz użyć 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

Zduplikowana obsługa

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

Zwróci tablicę logiczną, jeśli nieciągłe wyniki

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)

Wykorzystuje wewnętrznie hashtable, więc szybko

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

Jak wskazuje Viktor, tworzenie indeksu wiąże się z jednorazowym narzutem (powstaje, gdy faktycznie ZROBISZ coś z indeksem, np. 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
źródło
1
@Jeff, jeśli masz bardziej interesujący indeks, nie jest to takie łatwe ... ale myślę, że możesz to zrobićs.index[_]
Andy Hayden
11
In [92]: (myseries==7).argmax()
Out[92]: 3

Działa to, jeśli wiesz z wyprzedzeniem, że jest tam 7. Możesz to sprawdzić za pomocą (myseries == 7) .any ()

Innym podejściem (bardzo podobnym do pierwszej odpowiedzi), które również uwzględnia wiele siódemek (lub żadnej), jest

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
źródło
Kwestia znajomości 7 jest elementem z góry. Jednak użycie anysprawdzenia nie jest idealne, ponieważ potrzebna jest podwójna iteracja. Jest fajny test post op, który ujawni wszystkie Falsewarunki, które możesz zobaczyć tutaj .
jxramos
1
Ostrożnie, jeśli żaden element nie argmaxspełnia tego warunku, nadal zwróci 0 (zamiast błędu).
cs95
8

Jestem pod wrażeniem wszystkich odpowiedzi tutaj. To nie jest nowa odpowiedź, tylko próba podsumowania czasów wszystkich tych metod. Rozważyłem przypadek serii z 25 elementami i przyjąłem ogólny przypadek, w którym indeks może zawierać dowolne wartości i chcesz, aby wartość indeksu odpowiadała wartości wyszukiwania, która znajduje się pod koniec serii.

Oto testy szybkości na MacBooku Pro 2013 w Pythonie 3.7 z wersją Pandas 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)

Odpowiedź @ Jeffa wydaje się być najszybsza - chociaż nie obsługuje duplikatów.

Korekta : Przepraszam, przegapiłem jedno, rozwiązanie @Alex Spangher wykorzystujące metodę indeksu listy jest zdecydowanie najszybsze.

Aktualizacja : Dodano odpowiedź @ EliadL.

Mam nadzieję że to pomoże.

Niesamowite, że tak prosta operacja wymaga tak zawiłych rozwiązań, a wiele z nich jest tak powolnych. W niektórych przypadkach ponad pół milisekundy, aby znaleźć wartość w serii 25.

Rachunek
źródło
1
Dzięki. Ale czy nie powinieneś mierzyć po myindex utworzeniu, skoro trzeba go utworzyć tylko raz?
EliadL
Można by się z tym spierać, ale to zależy od tego, ile takich wyszukiwań jest wymaganych. myindexSerię warto utworzyć tylko wtedy, gdy zamierzasz wielokrotnie wyszukiwać. Do tego testu założyłem, że był potrzebny tylko raz, a całkowity czas wykonania był ważny.
Bill
1
Właśnie dziś wieczorem wpadłem na tę potrzebę i użycie .get_lock () na tym samym obiekcie Index w wielu wyszukiwaniach wydaje się być najszybsze. Myślę, że ulepszeniem odpowiedzi byłoby podanie czasu dla obu: w tym tworzenia indeksu i innego czasu tylko wyszukiwania po jego utworzeniu.
Rick wspiera Monikę
Tak, słuszna uwaga. @EliadL również to powiedział. Zależy to od liczby aplikacji, w których seria jest statyczna. Jeśli jakiekolwiek wartości w serii ulegną zmianie, należy przebudować pd.Index(myseries). Aby być uczciwym w stosunku do innych metod, założyłem, że oryginalna seria mogła ulec zmianie od ostatniego wyszukiwania.
Bill
5

Innym sposobem, aby to zrobić, choć równie niezadowalającym, jest:

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

list(s).index(7)

zwraca: 3

Testy na czas przy użyciu aktualnego zbioru danych, z którym pracuję (rozważ to losowo):

[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
źródło
4

Jeśli używasz numpy, możesz uzyskać tablicę gatunków, w których znajduje się twoja wartość:

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)

Zwraca jednoelementową krotkę zawierającą tablicę indecies, gdzie 7 jest wartością w myseries:

(array([3], dtype=int64),)
Alex
źródło
3

możesz użyć 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
źródło
5
Wydaje się, że zwraca to tylko indeks, w którym znajduje się element max, a nie konkretny, index of certain elementtaki jak zadane pytanie.
jxramos
1

Innym sposobem, aby to zrobić, o którym jeszcze nie wspomniano, jest metoda tolist:

myseries.tolist().index(7)

powinien zwrócić poprawny indeks, zakładając, że wartość istnieje w Serii.

rmutalik
źródło
1
@Alex Spangher zasugerował coś podobnego 17 września 2014 roku. Zobacz jego odpowiedź. Dodałem teraz obie wersje do wyników testu.
Bill
0

Często twoja wartość występuje przy wielu indeksach:

>>> 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
źródło
0

To najbardziej natywne i skalowalne podejście, jakie udało mi się znaleźć:

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

>>> myindex[7]
3

>>> myindex[[7, 5, 7]]
7    3
5    4
7    3
dtype: int64
Eliad L.
źródło