Найти индекс элемента в серии Панды

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)

но я предполагаю, что должен быть лучший способ. Здесь?

sashkello
источник

Ответы:

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

Хотя я признаю, что должен быть лучший способ сделать это, но это по крайней мере позволяет избежать итерации и циклического перемещения по объекту и перемещает его на уровень C.

Виктор Керкез
источник
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
@ Джефф, если у вас есть более интересный индекс, это не так просто ... но я думаю, вы можете просто сделать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']
Alon
источник
Точка знания 7 - это элемент заранее. Однако использование anyпроверки не является идеальным, поскольку необходима двойная итерация. Есть классная проверка после операции, которая раскроет все Falseусловия, которые вы можете увидеть здесь .
jxramos
1
Осторожно, если ни один элемент не соответствует этому условию, argmaxвсе равно вернет 0 (вместо вывода из строя).
cs95
8

Я впечатлен всеми ответами здесь. Это не новый ответ, просто попытка обобщить время всех этих методов. Я рассмотрел случай ряда с 25 элементами и предположил общий случай, когда индекс может содержать любые значения, и вы хотите, чтобы значение индекса соответствовало значению поиска, которое находится ближе к концу ряда.

Вот тесты скорости на MacBook Pro 2013 года в Python 3.7 с версией 0.25.3 для Pandas.

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 - это значение в myseries:

(array([3], dtype=int64),)
Alex
источник
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
Похоже, это возвращает только индекс, в котором найден элемент max, а не конкретный, index of certain elementтакой как заданный вопрос.
jxramos
1

Другой способ сделать это, который еще не был упомянут, - это метод tolist:

myseries.tolist().index(7)

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

rmutalik
источник
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
EliadL
источник