Tìm chỉ số của phần tử trong Chuỗi gấu trúc


154

Tôi biết đây là một câu hỏi rất cơ bản nhưng vì một số lý do tôi không thể tìm thấy câu trả lời. Làm cách nào tôi có thể lấy chỉ mục của một số phần tử nhất định của Sê-ri trong gấu trúc trăn? (lần đầu tiên xuất hiện sẽ đủ)

Tức là tôi muốn một cái gì đó như:

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

Chắc chắn, có thể định nghĩa một phương thức như vậy với một vòng lặp:

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

print find(myseries, 7)

nhưng tôi cho rằng nên có một cách tốt hơn. Lanhung?

Câu trả lời:


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

Mặc dù tôi thừa nhận rằng nên có một cách tốt hơn để làm điều đó, nhưng điều này ít nhất tránh được việc lặp và lặp qua đối tượng và di chuyển nó đến cấp độ C.


12
Vấn đề ở đây là nó giả định rằng phần tử đang được tìm kiếm thực sự nằm trong danh sách. Đó là một con gấu trúc khổng lồ dường như không có hoạt động tìm kiếm tích hợp.
jxramos

7
Giải pháp này chỉ hoạt động nếu chuỗi của bạn có chỉ số nguyên liên tiếp. Nếu chỉ số chuỗi của bạn là theo thời gian, điều này không hoạt động.
Andrew Medlin

43

Chuyển đổi sang một Chỉ mục, bạn có thể sử dụng 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

Xử lý trùng lặp

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

Sẽ trả về một mảng boolean nếu trả về không liền kề

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)

Sử dụng một hashtable trong nội bộ, rất nhanh

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

Như Viktor chỉ ra, có một chi phí tạo một lần để tạo một chỉ mục (nó phát sinh khi bạn thực sự làm gì đó với chỉ mục, ví dụ 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 nếu bạn có một chỉ số thú vị hơn thì không dễ như vậy ... nhưng tôi đoán bạn chỉ có thể làm đượcs.index[_]
Andy Hayden

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

Điều này hoạt động nếu bạn biết 7 là có trước. Bạn có thể kiểm tra điều này với (myseries == 7) .any ()

Một cách tiếp cận khác (rất giống với câu trả lời đầu tiên) cũng chiếm nhiều 7 (hoặc không có) là

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

Điểm về việc biết 7 là một yếu tố trước là đúng. Tuy nhiên, sử dụng anykiểm tra là không lý tưởng vì cần phải lặp lại gấp đôi. Có một bài kiểm tra tuyệt vời op sẽ tiết lộ tất cả các Falseđiều kiện bạn có thể thấy ở đây .
jxramos

1
Cẩn thận, nếu không có phần tử nào khớp với điều kiện này, argmaxvẫn sẽ trả về 0 (thay vì lỗi).
cs95

8

Tôi ấn tượng với tất cả các câu trả lời ở đây. Đây không phải là một câu trả lời mới, chỉ là một nỗ lực để tóm tắt thời gian của tất cả các phương pháp này. Tôi đã xem xét trường hợp của một chuỗi có 25 phần tử và giả sử trường hợp chung trong đó chỉ mục có thể chứa bất kỳ giá trị nào và bạn muốn giá trị chỉ mục tương ứng với giá trị tìm kiếm nằm ở cuối của chuỗi.

Dưới đây là các bài kiểm tra tốc độ trên MacBook Pro 2013 trong Python 3.7 với phiên bản 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)

Câu trả lời của @ Jeff dường như là nhanh nhất - mặc dù nó không xử lý các bản sao.

Sửa chữa : Xin lỗi, tôi đã bỏ lỡ một, giải pháp của @Alex Spangher sử dụng phương pháp chỉ mục danh sách là nhanh nhất.

Cập nhật : Đã thêm câu trả lời của @ EliadL.

Hi vọng điêu nay co ich.

Thật ngạc nhiên khi một hoạt động đơn giản như vậy đòi hỏi các giải pháp phức tạp như vậy và nhiều người rất chậm. Hơn nửa mili giây trong một số trường hợp để tìm giá trị trong chuỗi 25.


1
Cảm ơn. Nhưng bạn không nên đo sau khi myindex được tạo, vì nó chỉ cần được tạo một lần?
EliadL

Bạn có thể lập luận rằng nhưng nó phụ thuộc vào số lượng tìm kiếm như thế này là bắt buộc. Nó chỉ đáng để tạo ra bộ myindextruyện nếu bạn sẽ tìm kiếm nhiều lần. Đối với thử nghiệm này, tôi cho rằng nó chỉ cần một lần và tổng thời gian thực hiện là quan trọng.
Hóa đơn

1
Chỉ cần thực hiện điều này tối nay và sử dụng .get_lock () trên cùng một đối tượng Index trên nhiều tra cứu có vẻ như là cách nhanh nhất. Tôi nghĩ rằng một cải tiến cho câu trả lời sẽ là cung cấp thời gian cho cả hai: bao gồm cả việc tạo Chỉ mục và thời gian khác chỉ tìm kiếm sau khi nó được tạo.
Rick hỗ trợ Monica

Vâng, điểm tốt. @EliadL cũng nói thế. Nó phụ thuộc vào số lượng ứng dụng của chuỗi là tĩnh. Nếu bất kỳ giá trị nào trong chuỗi thay đổi, bạn cần xây dựng lại pd.Index(myseries). Để công bằng với các phương pháp khác, tôi cho rằng loạt ban đầu có thể đã thay đổi kể từ lần tra cứu cuối cùng.
Hóa đơn

5

Một cách khác để làm điều này, mặc dù không thỏa mãn là:

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

list(s).index(7)

trả về: 3

Về kiểm tra thời gian bằng cách sử dụng bộ dữ liệu hiện tại tôi đang làm việc (coi đó là ngẫu nhiên):

[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

Nếu bạn sử dụng numpy, bạn có thể nhận được một loạt các phần tử mà giá trị của bạn được tìm thấy:

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)

Điều này trả về một bộ phần tử chứa một mảng các phần tử trong đó 7 là giá trị trong myseries:

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

3

bạn có thể sử dụng Series.idxmax ()

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

5
Điều này dường như chỉ trả về chỉ mục nơi tìm thấy phần tử tối đa, không phải là một index of certain elementcâu hỏi cụ thể như câu hỏi được hỏi.
jxramos

1

Một cách khác để làm điều đó chưa được đề cập đến là phương pháp khoan dung:

myseries.tolist().index(7)

sẽ trả về chỉ mục chính xác, giả sử giá trị tồn tại trong Sê-ri.


1
@Alex Spangher đã đề xuất một cái gì đó tương tự vào ngày 17 tháng 9 năm 14. Xem câu trả lời của anh ấy. Bây giờ tôi đã thêm cả hai phiên bản vào kết quả thử nghiệm.
Hóa đơn

0

Thông thường giá trị của bạn xảy ra ở nhiều chỉ số:

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

Đây là cách tiếp cận tự nhiên và có thể mở rộng nhất mà tôi có thể tìm thấy:

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

>>> myindex[7]
3

>>> myindex[[7, 5, 7]]
7    3
5    4
7    3
dtype: int64
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.