Có một hàm NumPy để trả về chỉ mục đầu tiên của một cái gì đó trong một mảng không?


Câu trả lời:


522

Có, đây là câu trả lời cho một mảng NumPy arrayvà giá trị itemđể tìm kiếm:

itemindex = numpy.where(array==item)

Kết quả là một tuple với tất cả các chỉ mục hàng, sau đó tất cả các chỉ mục cột.

Ví dụ: nếu một mảng có hai chiều và nó chứa mục của bạn tại hai vị trí thì

array[itemindex[0][0]][itemindex[1][0]]

sẽ bằng với mặt hàng của bạn và vì vậy sẽ

array[itemindex[0][1]][itemindex[1][1]]

numpy.where


1
Nếu bạn đang tìm kiếm hàng đầu tiên trong đó một mục tồn tại trong cột đầu tiên, thì mục này hoạt động (mặc dù nó sẽ gây ra lỗi chỉ mục nếu không tồn tại)rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
BrT

28
Điều gì nếu bạn muốn nó ngừng tìm kiếm sau khi tìm thấy giá trị đầu tiên? Tôi không nghĩ nơi () có thể so sánh để tìm ()
Michael Clerx

2
Ah! Nếu bạn quan tâm đến hiệu suất, hãy xem câu trả lời cho câu hỏi này: stackoverflow.com/questions/7632963/ mẹo
Michael Clerx

11
np.argwheresẽ hữu ích hơn một chút ở đây:itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Eric

3
Điều đáng chú ý là câu trả lời này giả sử mảng là 2D. wherehoạt động trên bất kỳ mảng nào và sẽ trả về một tuple có độ dài 3 khi được sử dụng trên mảng 3D, v.v.
P. Camilleri

69

Nếu bạn cần chỉ mục của lần xuất hiện đầu tiên chỉ có một giá trị , bạn có thể sử dụng nonzero(hoặc where, tương đương với điều tương tự trong trường hợp này):

>>> t = array([1, 1, 1, 2, 2, 3, 8, 3, 8, 8])
>>> nonzero(t == 8)
(array([6, 8, 9]),)
>>> nonzero(t == 8)[0][0]
6

Nếu bạn cần chỉ mục đầu tiên của mỗi trong số nhiều giá trị , rõ ràng bạn có thể làm tương tự như trên nhiều lần, nhưng có một mẹo có thể nhanh hơn. Sau đây tìm thấy các chỉ số của phần tử đầu tiên của mỗi phần sau :

>>> nonzero(r_[1, diff(t)[:-1]])
(array([0, 3, 5, 6, 7, 8]),)

Lưu ý rằng nó tìm thấy sự khởi đầu của cả hai chuỗi 3 và cả hai sau 8 giây:

[ 1 , 1, 1, 2 , 2, 3 , 8 , 3 , 8 , 8]

Vì vậy, nó hơi khác so với việc tìm sự xuất hiện đầu tiên của mỗi giá trị. Trong chương trình của bạn, bạn có thể làm việc với một phiên bản được sắp xếp tđể có được những gì bạn muốn:

>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)

4
Bạn có thể vui lòng giải thích những gì r_được?
Geoff

1
@Geoff, r_nối; hoặc, chính xác hơn, nó dịch các đối tượng lát thành ghép dọc theo mỗi trục. Tôi có thể đã sử dụng hstackthay thế; điều đó có thể đã ít gây nhầm lẫn. Xem tài liệu để biết thêm thông tin về r_. Ngoài ra còn có a c_.
Vebjorn Ljosa

+1, một trong những tốt đẹp! (vs NP.where) giải pháp của bạn là đơn giản hơn rất nhiều (và có lẽ nhanh hơn) trong trường hợp nó chỉ xuất hiện đầu tiên của một giá trị được đưa ra trong một mảng 1D mà chúng ta cần
Doug

3
Trường hợp thứ hai (tìm chỉ mục đầu tiên của tất cả các giá trị) được đưa ra bởivals, locs = np.unique(t, return_index=True)
askewchan

50

Bạn cũng có thể chuyển đổi một mảng NumPy thành danh sách trên không và lấy chỉ mục của nó. Ví dụ,

l = [1,2,3,4,5] # Python list
a = numpy.array(l) # NumPy array
i = a.tolist().index(2) # i will return index of 2
print i

Nó sẽ in 1.


Nó có thể là thư viện đã thay đổi kể từ khi nó được viết lần đầu tiên. Nhưng đây là giải pháp đầu tiên hiệu quả với tôi.
amracel

1
Tôi đã sử dụng tốt điều này để tìm nhiều giá trị trong danh sách bằng cách hiểu danh sách:[find_list.index(index_list[i]) for i in range(len(index_list))]
Matt Wenham

1
@Matt Wenham Nếu nó đủ lớn, bạn có thể chuyển đổi find_listthành một mảng NumPy object(hoặc bất cứ điều gì cụ thể hơn phù hợp) và chỉ cần làm find_arr[index_list].
Narfanar

Hoàn toàn lạc đề, nhưng đây là lần đầu tiên tôi thấy cụm từ "trên không" - thứ mà tôi thấy nhiều nhất, ở vị trí của nó, có lẽ là "đang bay".
Flow2k

18

Chỉ cần thêm một hiệu suất rất cao và tiện dụng thay thế dựa trên np.ndenumerateđể tìm chỉ số đầu tiên:

from numba import njit
import numpy as np

@njit
def index(array, item):
    for idx, val in np.ndenumerate(array):
        if val == item:
            return idx
    # If no item was found return None, other return types might be a problem due to
    # numbas type inference.

Điều này khá nhanh và xử lý một cách tự nhiên với các mảng đa chiều :

>>> arr1 = np.ones((100, 100, 100))
>>> arr1[2, 2, 2] = 2

>>> index(arr1, 2)
(2, 2, 2)

>>> arr2 = np.ones(20)
>>> arr2[5] = 2

>>> index(arr2, 2)
(5,)

Điều này có thể nhanh hơn nhiều (vì nó ngắn mạch hoạt động) so với bất kỳ phương pháp nào sử dụng np.wherehoặcnp.nonzero .


Tuy nhiên np.argwherecũng có thể xử lý một cách duyên dáng với các mảng đa chiều (bạn sẽ cần phải tự chuyển nó thành một tuple nó không bị ngắn mạch) nhưng sẽ thất bại nếu không tìm thấy kết quả khớp:

>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)

2
@njitlà một tốc ký jit(nopython=True)tức là hàm sẽ được biên dịch đầy đủ khi đang chạy lần đầu tiên để các lệnh gọi trình thông dịch Python được loại bỏ hoàn toàn.
bartolo-otrit

14

Nếu bạn sẽ sử dụng điều này như một chỉ mục vào một thứ khác, bạn có thể sử dụng các chỉ số boolean nếu các mảng có thể phát được; bạn không cần chỉ số rõ ràng. Cách đơn giản tuyệt đối để làm điều này là chỉ đơn giản dựa trên chỉ số dựa trên giá trị thật.

other_array[first_array == item]

Bất kỳ hoạt động boolean hoạt động:

a = numpy.arange(100)
other_array[first_array > 50]

Phương pháp nonzero cũng có booleans:

index = numpy.nonzero(first_array == item)[0][0]

Hai số không dành cho bộ chỉ số (giả sử First_array là 1D) và sau đó là mục đầu tiên trong mảng chỉ số.


10

l.index(x)trả về i nhỏ nhất sao cho i là chỉ số xuất hiện đầu tiên của x trong danh sách.

Người ta có thể giả định một cách an toàn rằng index()hàm trong Python được triển khai để nó dừng lại sau khi tìm thấy kết quả khớp đầu tiên và điều này dẫn đến hiệu suất trung bình tối ưu.

Để tìm phần tử dừng sau trận đấu đầu tiên trong mảng NumPy, hãy sử dụng một trình vòng lặp ( ndenum Cả ).

In [67]: l=range(100)

In [68]: l.index(2)
Out[68]: 2

Mảng NumPy:

In [69]: a = np.arange(100)

In [70]: next((idx for idx, val in np.ndenumerate(a) if val==2))
Out[70]: (2L,)

Lưu ý rằng cả hai phương thức index()nexttrả về một lỗi nếu không tìm thấy phần tử. Với next, người ta có thể sử dụng một đối số thứ hai để trả về một giá trị đặc biệt trong trường hợp không tìm thấy phần tử, vd

In [77]: next((idx for idx, val in np.ndenumerate(a) if val==400),None)

Có các hàm khác trong NumPy ( argmax, wherenonzero) có thể được sử dụng để tìm một phần tử trong một mảng, nhưng tất cả chúng đều có nhược điểm là đi qua toàn bộ mảng tìm kiếm tất cả các lần xuất hiện, do đó không được tối ưu hóa để tìm phần tử đầu tiên. Cũng lưu ý rằng wherenonzerotrả về mảng, vì vậy bạn cần chọn phần tử đầu tiên để lấy chỉ mục.

In [71]: np.argmax(a==2)
Out[71]: 2

In [72]: np.where(a==2)
Out[72]: (array([2], dtype=int64),)

In [73]: np.nonzero(a==2)
Out[73]: (array([2], dtype=int64),)

So sánh thời gian

Chỉ cần kiểm tra xem các mảng lớn, giải pháp sử dụng iterator sẽ nhanh hơn khi mục được tìm kiếm ở đầu mảng (sử dụng %timeittrong vỏ IPython):

In [285]: a = np.arange(100000)

In [286]: %timeit next((idx for idx, val in np.ndenumerate(a) if val==0))
100000 loops, best of 3: 17.6 µs per loop

In [287]: %timeit np.argmax(a==0)
1000 loops, best of 3: 254 µs per loop

In [288]: %timeit np.where(a==0)[0][0]
1000 loops, best of 3: 314 µs per loop

Đây là một vấn đề NumPy GitHub mở .

Xem thêm: Numpy: tìm chỉ số đầu tiên của giá trị nhanh


1
Tôi nghĩ bạn cũng nên bao gồm thời gian cho trường hợp xấu nhất (yếu tố cuối cùng) để người đọc biết điều gì xảy ra với họ trong trường hợp xấu nhất khi họ sử dụng phương pháp của bạn.
MSeifert

@MSeifert Tôi không thể có thời gian hợp lý cho giải pháp lặp lại trường hợp xấu nhất - Tôi sẽ xóa câu trả lời này cho đến khi tôi phát hiện ra điều gì sai với nó
user2314737

1
không %timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))làm việc Nếu bạn đang tự hỏi tại sao nó chậm hơn 1000 lần - thì đó là vì các vòng lặp trăn trên các mảng numpy nổi tiếng là chậm.
MSeifert

@MSeifert không tôi không biết điều đó, nhưng tôi cũng bối rối vì thực tế đó argmaxwherenhanh hơn nhiều trong trường hợp này (yếu tố được tìm kiếm ở cuối mảng)
user2314737

Chúng phải nhanh như thể phần tử ở đầu. Họ luôn xử lý toàn bộ mảng để họ luôn mất cùng thời gian (ít nhất là họ nên).
MSeifert

9

Đối với các mảng được sắp xếp một chiều , việc sử dụng numpy.searchsort sẽ trả về số nguyên NumPy (vị trí) sẽ đơn giản và hiệu quả hơn nhiều . Ví dụ,

arr = np.array([1, 1, 1, 2, 3, 3, 4])
i = np.searchsorted(arr, 3)

Chỉ cần chắc chắn rằng mảng đã được sắp xếp

Ngoài ra, hãy kiểm tra xem chỉ mục được trả về tôi có thực sự chứa phần tử được tìm kiếm hay không, vì mục tiêu chính của tìm kiếm là tìm các chỉ mục nơi các phần tử nên được chèn để duy trì trật tự.

if arr[i] == 3:
    print("present")
else:
    print("not present")

2
tìm kiếm không phải là nlog (n) vì nó không sắp xếp mảng trước khi tìm kiếm, nó giả định rằng mảng đối số đã được sắp xếp. xem tài liệu của numpy.searchsort (liên kết ở trên)
Alok Nayak

6

Để lập chỉ mục cho bất kỳ tiêu chí nào, bạn có thể làm như vậy như sau:

In [1]: from numpy import *
In [2]: x = arange(125).reshape((5,5,5))
In [3]: y = indices(x.shape)
In [4]: locs = y[:,x >= 120] # put whatever you want in place of x >= 120
In [5]: pts = hsplit(locs, len(locs[0]))
In [6]: for pt in pts:
   .....:         print(', '.join(str(p[0]) for p in pt))
4, 4, 0
4, 4, 1
4, 4, 2
4, 4, 3
4, 4, 4

Và đây là một hàm nhanh để thực hiện những gì list.index () làm, ngoại trừ không đưa ra một ngoại lệ nếu không tìm thấy. Coi chừng - điều này có lẽ rất chậm trên các mảng lớn. Bạn có thể có thể sửa lỗi này thành mảng nếu bạn muốn sử dụng nó như một phương pháp.

def ndindex(ndarray, item):
    if len(ndarray.shape) == 1:
        try:
            return [ndarray.tolist().index(item)]
        except:
            pass
    else:
        for i, subarray in enumerate(ndarray):
            try:
                return [i] + ndindex(subarray, item)
            except:
                pass

In [1]: ndindex(x, 103)
Out[1]: [4, 0, 3]

5

Đối với mảng 1D, tôi khuyên bạn nên np.flatnonzero(array == value)[0], tương đương với cả hai np.nonzero(array == value)[0][0]np.where(array == value)[0][0]tránh sự xấu xí của việc hủy hộp dữ liệu 1 phần tử.


4

Một cách khác để chọn phần tử đầu tiên từ np.where () là sử dụng biểu thức trình tạo cùng với liệt kê, chẳng hạn như:

>>> import numpy as np
>>> x = np.arange(100)   # x = array([0, 1, 2, 3, ... 99])
>>> next(i for i, x_i in enumerate(x) if x_i == 2)
2

Đối với một mảng hai chiều, người ta sẽ làm:

>>> x = np.arange(100).reshape(10,10)   # x = array([[0, 1, 2,... 9], [10,..19],])
>>> next((i,j) for i, x_i in enumerate(x) 
...            for j, x_ij in enumerate(x_i) if x_ij == 2)
(0, 2)

Ưu điểm của phương pháp này là nó dừng kiểm tra các phần tử của mảng sau khi tìm thấy kết quả khớp đầu tiên, trong khi np.where kiểm tra tất cả các phần tử cho một kết quả khớp. Một biểu thức trình tạo sẽ nhanh hơn nếu có kết quả sớm trong mảng.


Trong trường hợp có thể không có sự trùng khớp nào trong mảng, phương pháp này cũng cho phép bạn thuận tiện chỉ định giá trị dự phòng. Nếu ví dụ đầu tiên là trở lại Nonenhư một dự phòng, nó sẽ trở thành next((i for i, x_i in enumerate(x) if x_i == 2), None).
Erlend Magnus Viggen

4

Có rất nhiều hoạt động trong NumPy có lẽ có thể được kết hợp để thực hiện điều này. Điều này sẽ trả về các chỉ số của các phần tử bằng với mục:

numpy.nonzero(array - item)

Sau đó, bạn có thể lấy các yếu tố đầu tiên của danh sách để có được một yếu tố duy nhất.


5
sẽ không cung cấp cho các chỉ số của tất cả các yếu tố không bằng với mặt hàng?
Autoplectic

3

Các numpy_indexed gói (từ chối trách nhiệm, tôi tác giả của nó) có chứa một tương đương vector hóa của list.index cho numpy.ndarray; đó là:

sequence_of_arrays = [[0, 1], [1, 2], [-5, 0]]
arrays_to_query = [[-5, 0], [1, 0]]

import numpy_indexed as npi
idx = npi.indices(sequence_of_arrays, arrays_to_query, missing=-1)
print(idx)   # [2, -1]

Giải pháp này có hiệu suất véc tơ, khái quát hóa cho các thông số và có nhiều cách khác nhau để xử lý các giá trị còn thiếu.


-1

Lưu ý: đây là phiên bản python 2.7

Bạn có thể sử dụng hàm lambda để giải quyết vấn đề và nó hoạt động cả trên mảng và danh sách NumPy.

your_list = [11, 22, 23, 44, 55]
result = filter(lambda x:your_list[x]>30, range(len(your_list)))
#result: [3, 4]

import numpy as np
your_numpy_array = np.array([11, 22, 23, 44, 55])
result = filter(lambda x:your_numpy_array [x]>30, range(len(your_list)))
#result: [3, 4]

Và bạn có thể sử dụng

result[0]

để có được chỉ số đầu tiên của các yếu tố được lọc.

Đối với python 3.6, sử dụng

list(result)

thay vì

result

Điều này dẫn <filter object at 0x0000027535294D30>đến Python 3 (đã thử nghiệm trên Python 3.6.3). Có lẽ cập nhật cho Python 3?
Peter Mortensen
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.