Bir dizideki bir şeyin ilk dizinini döndürmek için bir NumPy işlevi var mı?


Yanıtlar:


524

Evet, aramak için bir NumPy dizisi arrayve bir değer verilen cevap item:

itemindex = numpy.where(array==item)

Sonuç, önce tüm satır indekslerini, ardından tüm sütun indekslerini içeren bir demettir.

Örneğin, bir dizi iki boyutsa ve öğenizi iki konumda içeriyorsa,

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

öğenize eşit olur ve böylece

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

numpy.where


1
İlk sütunda bir öğenin bulunduğu ilk satırı arıyorsanız, bu işe yarar (ancak hiçbiri yoksa bir dizin hatası atar)rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
44'te BrT

29
İlk değeri bulduktan sonra aramayı durdurmasını isterseniz ne olur? ()
Michael Clerx

2
Ah! Performansla ilgileniyorsanız, bu sorunun
cevabına göz atın

11
np.argwhereburada biraz daha yararlı olurdu:itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Eric

3
Bu cevabın dizinin 2B olduğunu varsaydığını belirtmek gerekir. whereherhangi bir dizi üzerinde çalışır ve bir 3D dizi, vb. kullanıldığında uzunluk 3 bir demet dönecektir.
P. Camilleri

70

Yalnızca bir değerin ilk oluşumunun dizinine ihtiyacınız varsa, kullanabilirsiniz nonzero(veya wherebu durumda aynı şey anlamına gelir):

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

Birçok değerin her birinin ilk dizinine ihtiyacınız varsa , yukarıdakiyle aynı şekilde tekrar tekrar yapabilirsiniz, ancak daha hızlı olabilecek bir hile vardır. Aşağıda, her bir alt dizinin ilk öğesinin indeksleri bulunur :

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

Hem 3'lü dizinin hem de 8'li dizinin her ikisinin de başlangıcını bulduğuna dikkat edin:

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

Dolayısıyla , her bir değerin ilk oluşumunu bulmaktan biraz farklıdır . Programınızda, istediğinizi elde etmek için sıralı bir sürümüyle çalışabilirsiniz t:

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

4
Ne olduğunu açıklar r_mısınız?
Geoff

1
@ Geoff, r_bitiştirir; veya daha doğrusu, dilim nesnelerini her eksen boyunca birleştirmeye çevirir. Onun hstackyerine kullanmış olabilirdim ; bu daha az kafa karıştırıcı olabilir. Hakkında daha fazla bilgi için belgelere bakın r_. Ayrıca bir c_.
Vebjorn Ljosa

+1, iyi olan! çözüm bir 1D dizide belli bir değerin sadece ilk olay değil durumunda çok daha kolay (ve muhtemelen daha hızlı) 'dir (NP.where vs) ihtiyaç duyduğumuz
Doug

3
İkinci durum (tüm değerlerin ilk dizinini bulma)vals, locs = np.unique(t, return_index=True)
askewchan

@askewchan sürümünüz işlevsel olarak eşdeğer, ancak çok, çok, çok daha yavaş
Jivan

50

Ayrıca bir NumPy dizisini havadan listeye dönüştürebilir ve dizinini alabilirsiniz. Örneğin,

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

Yazdırılacaktır 1.


İlk yazıldığından beri kütüphane değişmiş olabilir. Ama bu benim için işe yarayan ilk çözümdü.
amracel

1
Bir liste kavrayışı kullanarak bir listede birden çok değer bulmak için bunu iyi kullandım:[find_list.index(index_list[i]) for i in range(len(index_list))]
Matt Wenham

1
@MattWenham Yeterince büyükse, find_listbir NumPy dizisine object(veya uygun olan daha spesifik bir şeye) dönüştürebilir ve yapabilirsiniz find_arr[index_list].
Narfanar

Tamamen konu dışı, ama bu ilk kez "havada" ifadesini görüyorum - onun yerine en çok gördüğüm şey muhtemelen "anında".
flow2k

18

Sadece çok performanslı ve kullanışlı np.ndenumerateilk dizini bulmak için alternatif :

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.

Bu oldukça hızlıdır ve çok boyutlu dizilerle doğal olarak ilgilenir :

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

Bu, veya tuşlarını kullanan herhangi bir yaklaşımdan çok daha hızlı olabilir (çünkü işlemi kısa devre yapar) .np.wherenp.nonzero


Bununla birlikte , çok boyutlu dizilerle np.argwherede zarif bir şekilde başa çıkabilir (manuel olarak bir tupa dökmeniz gerekir ve kısa devre yapmazsınız), ancak eşleşme bulunmazsa başarısız olur:

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

2
@njitbir kısaltmadır jit(nopython=True)işlev tamamen böylece çağrıları tercüman Python tümüyle kaldırıldığından emin ilk çalıştırma anında on-the-fly derlenmiş olacak yani.
bartolo-otrit

14

Bunu başka bir şeye dizin olarak kullanacaksanız, diziler yayınlanabilirse, boole dizinlerini kullanabilirsiniz; açık endekslere ihtiyacınız yoktur. Bunu yapmanın en basit yolu, bir doğruluk değerine dayalı olarak indekslemektir.

other_array[first_array == item]

Herhangi bir boole işlemi çalışır:

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

Sıfır olmayan yöntem de booleans alır:

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

İki sıfır, bir dizi indeks içindir (ilk_dizinin 1D olduğu varsayılarak) ve daha sonra indeksler dizisindeki ilk öğe içindir.


10

l.index(x)En küçük döndüren ı öyle ki ben listedeki x ilk geçtiği endeksidir.

index()Python'daki işlevin, ilk eşleşmeyi bulduktan sonra durması için uygulandığını güvenle varsayabiliriz ve bu da optimum ortalama performansla sonuçlanır.

NumPy dizisindeki ilk eşleşmeden sonra duran bir öğeyi bulmak için bir yineleyici ( ndenumerate ) kullanın.

In [67]: l=range(100)

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

NumPy dizisi:

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

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

Her iki yöntemin index()ve nextöğe bulunamazsa bir hata döndürdüğünü unutmayın . İle nextöğenin bulunmaması durumunda özel bir değer döndürmek için ikinci bir bağımsız değişken kullanılabilir.

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

NumPy ( argmax, whereve nonzero) öğesinde bir dizideki bir öğeyi bulmak için kullanılabilecek başka işlevler de vardır , ancak hepsinin tüm oluşumları arayan tüm diziyi geçme dezavantajı vardır , bu nedenle ilk öğeyi bulmak için optimize edilmez. Bunu da not edin whereve nonzerodizileri döndürün, böylece dizini almak için ilk öğeyi seçmeniz gerekir.

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

Zaman karşılaştırması

Büyük diziler için , aranan öğe dizinin%timeit başındayken ( IPython kabuğunda kullanarak) bir yineleyici kullanan çözümün daha hızlı olduğunu kontrol etmek yeterlidir:

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

Bu açık bir NumPy GitHub sorunudur .

Ayrıca bakınız: Numpy: değerin ilk dizinini hızlı bulun


1
Ayrıca, en kötü durum için bir zamanlama eklemeniz gerektiğini düşünüyorum (son öğe).
MSeifert

@MSeifert En kötü durum yineleyici çözümü için makul bir zamanlama alamıyorum - neyin yanlış olduğunu bulana kadar bu cevabı sileceğim
user2314737

1
çalışmıyor %timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))mu? Neden 1000 kat daha yavaş olduğunu merak ediyorsanız - bunun nedeni, sayısal diziler üzerindeki python döngülerinin kötü bir şekilde yavaş olmasıdır.
MSeifert

@ MSeifert hayır Bunu bilmiyordum, ama aynı zamanda argmaxve wherebu durumda çok daha hızlı
olduğumdan şaşkınım

Öğe başlangıçta olduğu kadar hızlı olmalıdırlar. Her zaman tüm diziyi işlerler, böylece her zaman aynı zamanı alırlar (en azından öyle olmalıdır).
MSeifert

9

Tek boyutlu sıralı diziler için, NumPy tamsayısı (konum) döndüren numpy.searchsorted kullanmak çok daha basit ve verimli O (log (n)) olacaktır . Örneğin,

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

Dizinin zaten sıralandığından emin olun

Ayrıca, döndürülen dizinin i gerçekten aranan öğeyi içerip içermediğini kontrol edin, çünkü searchsorted'ın ana hedefi düzeni korumak için öğelerin nereye ekleneceği dizinleri bulmaktır.

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

2
searchsorted, diziyi aramadan önce sıralamadığından nlog (n) değildir, argüman dizisinin zaten sıralandığını varsayar. numpy.searchsorted (yukarıdaki bağlantı) belgelerine göz atın
Alok Nayak

6

Herhangi bir ölçüt üzerinde dizin oluşturmak için aşağıdakine benzer bir şey yapabilirsiniz:

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

Ve işte list.index () 'in yaptığı şeyi yapmak için hızlı bir işlev, ancak bulunmazsa bir istisna oluşturmaz. Dikkat - bu büyük dizilerde muhtemelen çok yavaş. Bir yöntem olarak kullanmayı tercih ederseniz, bunu diziler için maymun yaması yapabilirsiniz.

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

1D diziler için, np.flatnonzero(array == value)[0]her ikisine eşdeğer olan np.nonzero(array == value)[0][0]ve np.where(array == value)[0][0]ancak 1 elemanlı bir demet açmanın çirkinliğinden kaçınan tavsiye ederim .


4

Np.where () öğesinden ilk öğeyi seçmenin bir alternatifi, aşağıdaki gibi bir üreteç ifadesini numaralandırma ile birlikte kullanmaktır:

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

İki boyutlu bir dizi için:

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

Bu yaklaşımın avantajı, ilk eşleşme bulunduktan sonra dizinin öğelerini kontrol etmeyi durdurması, oysa np.where tüm eşleşme için öğeleri kontrol etmesidir. Dizinin başlarında eşleşme varsa bir üreteç ifadesi daha hızlı olur.


Dizide hiç bir eşleşme olmayabilirse, bu yöntem ayrıca kolayca bir geri dönüş değeri belirlemenizi sağlar. İlk örnek Nonebir yedek olarak geri dönecek olsaydı, bu olur next((i for i, x_i in enumerate(x) if x_i == 2), None).
Erlend Magnus Viggen

4

NumPy'de bunu başarmak için bir araya getirilebilecek birçok işlem var. Bu, öğeye eşit elemanların endekslerini döndürür:

numpy.nonzero(array - item)

Daha sonra tek bir öğe almak için listelerin ilk öğelerini alabilirsiniz.


5
bu, öğeye eşit olmayan tüm öğelerin endekslerini vermeyecek midir?
Autoplectic

3

Numpy_indexed paketi (reddi, onun yazarım) numpy.ndarray için list.index bir vectorized eşdeğerini içermektedir; yani:

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]

Bu çözüm performansı vektörleştirdi, ndarralara genelleşti ve eksik değerlerle başa çıkmanın çeşitli yollarına sahip.


-1

Not: bu python 2.7 sürümü için

Sorunla başa çıkmak için bir lambda işlevi kullanabilirsiniz ve hem NumPy dizisi hem de liste üzerinde çalışır.

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]

Ve kullanabilirsiniz

result[0]

filtrelenen öğelerin ilk dizinini almak için.

Python 3.6 için şunu kullanın:

list(result)

onun yerine

result

Bu <filter object at 0x0000027535294D30>Python 3 ile sonuçlanır (Python 3.6.3 üzerinde test edilmiştir). Belki de Python 3 için güncelleme?
Peter Mortensen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.