Знайдіть індекс елемента в серії pandas


154

Я знаю, що це дуже основне питання, але я чомусь не можу знайти відповідь. Як я можу отримати індекс певного елемента серії в пандах python? (перше виникнення буде достатньо)

Тобто, мені б хотілося чогось типу:

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

Безумовно, такий спосіб можна визначити за допомогою циклу:

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

print find(myseries, 7)

але я припускаю, що має бути кращий спосіб. Є там?

Відповіді:


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

Хоча я визнаю, що повинен бути кращий спосіб зробити це, але це, принаймні, уникає ітерації та перекидання через об'єкт та переміщення його на рівень С.


12
Проблема тут полягає в тому, що він передбачає, що елемент, який шукають, є насправді в списку. Це, здається, не має вбудованих операцій пошуку.
jxramos

7
Це рішення працює лише в тому випадку, якщо у вашої серії є послідовний цілий індекс. Якщо ваш індекс серії за датою, це не працює.
Ендрю Медлін

43

Перетворившись на індекс, ви можете використовувати 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

Дублювання обробки

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

Поверне булевий масив, якщо повернеться безперервний

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)

Використовує хештейн всередині, так швидко

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

Як зазначає Віктор, існує одноразове накладне створення індексу (воно виникає, коли ви дійсно щось робите з індексом, наприклад 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, якщо у вас є цікавіший індекс, це не зовсім так просто ... але, мабуть, ви просто можете це зробитиs.index[_]
Енді Хейден

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

Це працює, якщо ви знаєте, що 7 є заздалегідь. Ви можете перевірити це за допомогою (myseries == 7) .any ()

Інший підхід (дуже схожий на першу відповідь), який також враховує декілька 7 (або жоден)

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

Справа про те, що знати 7 - це заздалегідь елемент, - це правильно. Однак використання anyчека не є ідеальним, оскільки потрібна подвійна ітерація. Є класний пост перевірки, який відкриє всі Falseумови, які ви можете побачити тут .
jxramos

1
Обережно, якщо жоден елемент не відповідає цій умові, argmaxусе одно поверне 0 (замість помилки).
cs95

8

Я вражений усіма відповідями тут. Це не нова відповідь, а лише спроба узагальнити терміни всіх цих методів. Я розглянув випадок серії з 25 елементами і припустив загальний випадок, коли індекс міг містити будь-які значення, і ви хочете, щоб значення індексу відповідало значенню пошуку, яке знаходиться в кінці серії.

Ось тести швидкості на MacBook Pro 2013 року в Python 3.7 з версією 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)

@ Відповідь Джеффа здається найшвидшим - хоча він не обробляє дублікатів.

Виправлення : Вибачте, я пропустив рішення, рішення @Alex Spangher за допомогою методу списку індексу є найбільш швидким.

Оновлення : додано відповідь @ EliadL.

Сподіваюся, це допомагає.

Дивовижно, що така проста операція вимагає таких складних рішень, і багато хто так повільно. Більше половини мілісекунди в деяких випадках знайти значення в серії 25.


1
Дякую. Але ти не повинен вимірювати після myindex створення, оскільки потрібно лише створити один раз?
EliadL

Ви можете заперечити це, але це залежить від того, скільки таких пошукових запитів потрібно. Створювати myindexсеріал варто лише в тому випадку, якщо ви збираєтеся шукати багато разів. Для цього тесту я припускав, що це потрібно лише один раз, і загальний час виконання був важливим.
Білл

1
Щойно сьогодні ввечері виникло потреба в цьому, і використання .get_lock () на одному і тому ж об’єкті Index в декількох оглядах здається, що це повинно бути найшвидшим. Я думаю, що поліпшенням відповіді було б надання часу для обох: включаючи створення індексу та інший термін лише пошуку після його створення.
Рік підтримує Моніку

Так, хороший момент. @EliadL також сказав це. Це залежить від того, скільки застосувань у серії є статичними. Якщо якісь значення в серії змінюються, вам потрібно відновити pd.Index(myseries). Щоб бути справедливим до інших методів, я припускав, що початкова серія могла змінитися з моменту останнього пошуку.
Білл

5

Ще один спосіб зробити це, хоча не менш задовільно:

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

list(s).index(7)

повернення: 3

Тести часу, використовуючи поточний набір даних, з яким я працюю (вважай випадковим):

[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

Якщо ви використовуєте numpy, ви можете отримати масив невизначеностей, які знайдено ваше значення:

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)

Це повертає кордон з одним елементом, що містить масив невизначеностей, де 7 - значення в місеріях:

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

3

ви можете використовувати Series.idxmax ()

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

5
Це, здається, повертає індекс лише там, де знайдений максимальний елемент, а не конкретний, index of certain elementяк запитання.
jxramos

1

Ще один спосіб зробити це, про який ще не було сказано, - це метод списку:

myseries.tolist().index(7)

повинен повернути правильний індекс, вважаючи, що значення існує в Серії.


1
@Alex Spangher запропонував щось подібне 17 вересня 14 року. Дивіться його відповідь. Зараз я додав обидві версії до результатів тесту.
Білл

0

Часто ваше значення відбувається за кількома показниками:

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

Це найрідніший і масштабований підхід, який я міг знайти:

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

>>> myindex[7]
3

>>> myindex[[7, 5, 7]]
7    3
5    4
7    3
dtype: int64
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.