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?

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.


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

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

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.


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

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

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

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.


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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.