Apakah ada fungsi NumPy untuk mengembalikan indeks pertama dari sesuatu dalam array?

Jawaban:

523

Ya, inilah jawaban yang diberikan array NumPy array,, dan nilai item,, untuk mencari:

itemindex = numpy.where(array==item)

Hasilnya adalah tuple dengan pertama semua indeks baris, lalu semua indeks kolom.

Misalnya, jika array dua dimensi dan berisi item Anda di dua lokasi, maka

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

akan sama dengan item Anda dan begitu juga

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

numpy. Di mana saja

Alex
sumber
1
Jika Anda mencari baris pertama di mana item ada di kolom pertama, ini berfungsi (meskipun akan menyebabkan kesalahan indeks jika tidak ada)rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
BrT
29
Bagaimana jika Anda ingin berhenti mencari setelah menemukan nilai pertama? Saya tidak berpikir di mana () sebanding dengan menemukan ()
Michael Clerx
2
Ah! Jika Anda tertarik dengan kinerja, lihat jawaban untuk pertanyaan ini: stackoverflow.com/questions/7632963/…
Michael Clerx
11
np.argwhereakan sedikit lebih berguna di sini:itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Eric
3
Perlu dicatat bahwa jawaban ini menganggap array adalah 2D. wherebekerja pada array apa pun, dan akan mengembalikan tuple dengan panjang 3 saat digunakan pada array 3D, dll.
P. Camilleri
70

Jika Anda memerlukan indeks kemunculan pertama hanya satu nilai , Anda dapat menggunakan nonzero(atau where, yang jumlahnya sama dengan hal ini):

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

Jika Anda memerlukan indeks pertama dari masing-masing banyak nilai , Anda jelas dapat melakukan hal yang sama seperti di atas berulang kali, tetapi ada trik yang mungkin lebih cepat. Berikut ini menemukan indeks elemen pertama dari setiap berikutnya :

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

Perhatikan bahwa ia menemukan awal dari kedua urutan 3s dan kedua berikutnya dari 8s:

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

Jadi itu sedikit berbeda dari menemukan kemunculan pertama dari setiap nilai. Dalam program Anda, Anda mungkin dapat bekerja dengan versi yang diurutkan tuntuk mendapatkan yang Anda inginkan:

>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)
Vebjorn Ljosa
sumber
4
Bisakah Anda jelaskan apa r_itu?
Geoff
1
@ Geoff, r_menggabungkan; atau, lebih tepatnya, ia menerjemahkan objek irisan menjadi gabungan sepanjang setiap sumbu. Saya bisa menggunakannya hstacksebagai gantinya; yang mungkin kurang membingungkan. Lihat dokumentasi untuk informasi lebih lanjut tentang r_. Ada juga a c_.
Vebjorn Ljosa
+1, bagus! (vs NP.where) solusi Anda jauh lebih sederhana (dan mungkin lebih cepat) dalam kasus di mana itu hanya kemunculan pertama dari nilai yang diberikan dalam array 1D yang kita butuhkan
doug
3
Kasus terakhir (menemukan indeks pertama dari semua nilai) diberikan olehvals, locs = np.unique(t, return_index=True)
askewchan
@askewchan versi Anda secara fungsional setara, tapi jauh lebih lambat
Jivan
50

Anda juga dapat mengonversi array NumPy ke daftar di udara dan mendapatkan indeksnya. Sebagai contoh,

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

Ini akan mencetak 1.

Hima
sumber
Mungkin perpustakaan telah berubah sejak ini pertama kali ditulis. Tetapi ini adalah solusi pertama yang berhasil untuk saya.
amracel
1
Saya telah memanfaatkan ini untuk menemukan beberapa nilai dalam daftar menggunakan pemahaman daftar:[find_list.index(index_list[i]) for i in range(len(index_list))]
Matt Wenham
1
@ MatWenham Jika cukup besar, Anda dapat mengonversi Anda find_listke array NumPy object(atau apa pun yang lebih spesifik yang sesuai) dan lakukan saja find_arr[index_list].
Narfanar
Benar-benar di luar topik, tetapi ini adalah pertama kalinya saya melihat frasa "di udara" - apa yang paling sering saya lihat, sebagai gantinya, mungkin "on the fly".
flow2k
18

Hanya untuk menambahkan yang sangat performant dan praktis alternatif berdasarkan np.ndenumerateuntuk menemukan indeks pertama:

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.

Ini cukup cepat dan berhubungan secara alami dengan array multidimensi :

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

Ini bisa jauh lebih cepat (karena itu hubungan arus pendek operasi) daripada pendekatan apa pun menggunakan np.whereatau np.nonzero.


Namun np.argwheredapat juga menangani array multidimensi dengan anggun (Anda perlu secara manual melemparkannya ke tuple dan tidak mengalami hubungan pendek) tetapi akan gagal jika tidak ditemukan kecocokan:

>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)
MSeifert
sumber
2
@njitadalah singkatan jit(nopython=True)yaitu fungsi akan sepenuhnya dikompilasi on-the-fly pada saat menjalankan pertama sehingga panggilan juru bahasa Python sepenuhnya dihapus.
bartolo-otrit
14

Jika Anda akan menggunakan ini sebagai indeks ke sesuatu yang lain, Anda dapat menggunakan indeks boolean jika array dapat disiarkan; Anda tidak perlu indeks eksplisit. Cara termudah mutlak untuk melakukan ini adalah dengan hanya indeks berdasarkan nilai kebenaran.

other_array[first_array == item]

Semua operasi boolean berfungsi:

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

Metode bukan nol juga membutuhkan boolean:

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

Dua nol adalah untuk tupel indeks (dengan asumsi first_array adalah 1D) dan kemudian item pertama dalam array indeks.

Mat
sumber
10

l.index(x)mengembalikan i terkecil sehingga saya adalah indeks kemunculan pertama x dalam daftar.

Orang dapat dengan aman berasumsi bahwa index()fungsi dalam Python diimplementasikan sehingga berhenti setelah menemukan kecocokan pertama, dan ini menghasilkan kinerja rata-rata yang optimal.

Untuk menemukan elemen yang berhenti setelah pertandingan pertama dalam array NumPy gunakan iterator ( ndenumerate ).

In [67]: l=range(100)

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

Array NumPy:

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

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

Perhatikan bahwa kedua metode index()dan nextmengembalikan kesalahan jika elemen tidak ditemukan. Dengan next, seseorang dapat menggunakan argumen kedua untuk mengembalikan nilai khusus jika elemen tidak ditemukan, misalnya

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

Ada fungsi-fungsi lain di NumPy ( argmax,, wheredan nonzero) yang dapat digunakan untuk menemukan elemen dalam array, tetapi mereka semua memiliki kelemahan untuk menelusuri seluruh array untuk mencari semua kejadian, sehingga tidak dioptimalkan untuk menemukan elemen pertama. Perhatikan juga itu wheredan nonzerokembalikan array, jadi Anda harus memilih elemen pertama untuk mendapatkan indeks.

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

Perbandingan waktu

Hanya memeriksa bahwa untuk array besar, solusi menggunakan iterator lebih cepat ketika item yang dicari adalah di awal array (menggunakan %timeitdi shell 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

Ini adalah masalah NumPy GitHub terbuka .

Lihat juga: Numpy: cari indeks nilai pertama dengan cepat

pengguna2314737
sumber
1
Saya pikir Anda juga harus memasukkan waktu untuk kasus terburuk (elemen terakhir) supaya pembaca tahu apa yang terjadi pada mereka dalam kasus terburuk ketika mereka menggunakan pendekatan Anda.
MSeifert
@ MSeifert Saya tidak bisa mendapatkan waktu yang masuk akal untuk solusi iterator kasus terburuk - Saya akan menghapus jawaban ini sampai saya mengetahui apa yang salah dengan itu
user2314737
1
tidak %timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))bekerja? Jika Anda bertanya-tanya mengapa 1000 kali lebih lambat - itu karena loop python atas array numpy terkenal lambat.
MSeifert
@MSeifert tidak, saya tidak tahu itu, tapi saya juga bingung dengan kenyataan bahwa argmaxdan wherejauh lebih cepat dalam hal ini (elemen yang dicari di akhir array)
user2314737
Mereka harus secepat jika elemen di awal. Mereka selalu memproses seluruh array sehingga mereka selalu mengambil waktu yang sama (paling tidak seharusnya).
MSeifert
9

Untuk array yang diurutkan satu dimensi , akan jauh lebih sederhana dan efisien O (log (n)) untuk menggunakan numpy.searchsorted yang mengembalikan integer NumPy (posisi). Sebagai contoh,

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

Pastikan array sudah diurutkan

Juga periksa apakah indeks yang dikembalikan saya benar-benar berisi elemen yang dicari, karena tujuan utama pencarian adalah untuk menemukan indeks di mana elemen harus dimasukkan untuk menjaga ketertiban.

if arr[i] == 3:
    print("present")
else:
    print("not present")
Alok Nayak
sumber
2
searchsorted bukan nlog (n) karena tidak mengurutkan array sebelum pencarian, ini mengasumsikan bahwa array argumen sudah diurutkan. lihat dokumentasi numpy.searchsorted (tautan di atas)
Alok Nayak
6

Untuk mengindeks pada kriteria apa pun, Anda dapat melakukannya seperti berikut ini:

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

Dan inilah fungsi cepat untuk melakukan apa yang list.index () lakukan, kecuali tidak menimbulkan pengecualian jika tidak ditemukan. Waspadalah - ini mungkin sangat lambat pada array besar. Anda mungkin bisa menambal monyet ini ke array jika Anda lebih suka menggunakannya sebagai metode.

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]
Autoplektik
sumber
5

Untuk array 1D, saya akan merekomendasikan np.flatnonzero(array == value)[0], yang setara dengan keduanya np.nonzero(array == value)[0][0]dan np.where(array == value)[0][0]tetapi menghindari keburukan unboxing tuple 1-elemen.

1 ''
sumber
4

Alternatif untuk memilih elemen pertama dari np.where () adalah dengan menggunakan ekspresi generator bersama dengan penghitungan, seperti:

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

Untuk array dua dimensi yang akan dilakukan:

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

Keuntungan dari pendekatan ini adalah bahwa ia berhenti memeriksa elemen-elemen array setelah kecocokan pertama ditemukan, sedangkan np.where memeriksa semua elemen untuk kecocokan. Ekspresi generator akan lebih cepat jika ada kecocokan di awal array.

Noyer282
sumber
Jika tidak ada kecocokan dalam array sama sekali, metode ini juga memungkinkan Anda menentukan nilai cadangan. Jika contoh pertama dikembalikan Nonesebagai fallback, itu akan menjadi next((i for i, x_i in enumerate(x) if x_i == 2), None).
Erlend Magnus Viggen
4

Ada banyak operasi di NumPy yang mungkin bisa disatukan untuk mencapai ini. Ini akan mengembalikan indeks elemen yang sama dengan item:

numpy.nonzero(array - item)

Anda kemudian dapat mengambil elemen pertama dari daftar untuk mendapatkan elemen tunggal.

Ned Batchelder
sumber
5
bukankah itu memberikan indeks semua elemen yang tidak sama dengan item?
Autoplectic
3

The numpy_indexed paket (disclaimer, saya penulisnya) berisi setara Vectorized dari list.index untuk numpy.ndarray; itu adalah:

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]

Solusi ini memiliki kinerja vektor, digeneralisasikan ke ndarray, dan memiliki berbagai cara untuk menangani nilai yang hilang.

Eelco Hoogendoorn
sumber
-1

Catatan: ini untuk versi python 2.7

Anda dapat menggunakan fungsi lambda untuk mengatasi masalah, dan berfungsi baik pada array dan daftar 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]

Dan Anda bisa menggunakannya

result[0]

untuk mendapatkan indeks pertama dari elemen yang difilter.

Untuk python 3.6, gunakan

list(result)

dari pada

result
Statham
sumber
Ini menghasilkan <filter object at 0x0000027535294D30>pada Python 3 (diuji pada Python 3.6.3). Mungkin pembaruan untuk Python 3?
Peter Mortensen