NumPy 2d dizisinin dilimlenmesi veya nxn dizisinden (n> m) bir mxm alt matrisini nasıl ayıklayabilirim?


174

Bir NumPy nxn dizi dilim istiyorum. Yeni bir mxm dizi yapmak, m satır ve sütunlar bu dizinin (yani satır / sütun sayılarında herhangi bir desen olmadan) keyfi bir seçim ayıklamak istiyorum . Bu örnekte diyelim ki dizi 4x4 ve bundan 2x2 bir dizi çıkarmak istiyorum.

İşte dizimiz:

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

Kaldırılacak çizgi ve sütunlar aynı. En kolay durumda ben başında veya sonunda, yani bir 2x2 alt-matris çıkarmak istediğinizde yani:

In [33]: x[0:2,0:2]
Out[33]: 
array([[0, 1],
       [4, 5]])

In [34]: x[2:,2:]
Out[34]: 
array([[10, 11],
       [14, 15]])

Ancak başka bir satır / sütun karışımını kaldırmam gerekirse ne olur? Birinci ve üçüncü satırları / satırları kaldırmam ve böylece alt matrisi çıkarmam gerekirse [[5,7],[13,15]]ne olur ? Herhangi bir satır / çizgi bileşimi olabilir. Ben sadece diziler diziler ve sütunlar için hem satırları ve sütunları kullanarak dizini dizine gereken bir yerde okumak, ama bu çalışmıyor gibi görünüyor:

In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])

Bir yol buldum, ki:

    In [61]: x[[1,3]][:,[1,3]]
Out[61]: 
array([[ 5,  7],
       [13, 15]])

Bununla ilgili ilk sorun, bununla yaşayabilsem de, neredeyse okunabilir olmaması. Birinin daha iyi bir çözümü varsa, kesinlikle duymak isterim.

Başka bir şey dizileri diziler dizine NumPy istenen dizinin bir kopyasını yapmaya zorlar, böylece büyük diziler ile tedavi bu bir sorun haline gelebilir bir forumda okumak olduğunu. Bu neden böyle / bu mekanizma nasıl çalışıyor?

Yanıtlar:


62

Sven'in belirttiği gibi, x[[[0],[2]],[1,3]]1 ve 3 sütunlarla eşleşen 0 ve 2 satırı geri verirkenx[[0,2],[1,3]] geri , bir dizide x [0,1] ve x [2,3] değerlerini döndürür.

Verdiğim ilk örneği yapmak için yararlı bir işlev var numpy.ix_. İlk örneğimle aynı şeyi yapabilirsiniz x[numpy.ix_([0,2],[1,3])]. Bu, tüm bu ekstra parantezleri girmek zorunda kalmanıza engel olabilir.


111

Bu soruyu cevaplamak için, çok boyutlu bir dizinin endekslenmesinin Numpy'de nasıl çalıştığına bakmalıyız. Diyelim ki xsorunuzdan bir dizi aldınız . Atanan arabellek x0 ila 15 arasında 16 artan tamsayı içerecektir. Örneğin, bir öğeye x[i,j]erişiyorsanız, NumPy'nin bu öğenin arabellek başlangıcına göre bellek konumunu bulması gerekir. Bu, etkin bir şekilde hesaplanarak i*x.shape[1]+j(ve gerçek bir bellek ofseti elde etmek için bir int'in büyüklüğü ile çarpılarak yapılır ).

Bir alt diziyi temel dilimleme ile ayıklarsanız y = x[0:2,0:2], ortaya çıkan nesne alttaki arabelleği paylaşır x. Ama eğer erişirsen ne olur y[i,j]? NumPy i*y.shape[1]+j, diziye ofseti hesaplamak için kullanamaz , çünkü ait olan veriler ybellekte ardışık değildir.

NumPy bu sorunu adımlarla çözer . Erişim için bellek ofsetini x[i,j]hesaplarken, gerçekte hesaplanan şey i*x.strides[0]+j*x.strides[1](ve bu zaten bir int boyutu için faktörü içerir):

x.strides
(16, 4)

Tüm ygibi yukarıda ekstre edilir, NumPy yeni tampon oluşturmaz, ancak yapar aynı tamponu (aksi referans yeni bir dizi nesne oluşturmak ysadece eşit olacaktır xsonra.) Yeni bir dizi nesne farklı bir şekle sahip olacaktır xve belki de başka bir başlangıç ara belleğe kaydırılır, ancak adımlarla paylaşılır x(en azından bu durumda):

y.shape
(2,2)
y.strides
(16, 4)

Bu şekilde, bellek ofsetini hesaplamak y[i,j]doğru sonucu verecektir.

Ama NumPy gibi bir şey için ne yapmalı z=x[[1,3]] ? Adımlar mekanizması, orijinal tampon kullanılırsa doğru indekslemeye izin vermez z. NumPy teorik olarak olabilir adımlarla daha biraz daha sofistike bir mekanizma eklemek, ancak bu şekilde bir dizinin fikrini meydan okuyan, eleman erişim nispeten pahalı hale getirecektir. Ayrıca, bir görünüm artık gerçekten hafif bir nesne olmayacaktı.

Bu, indeksleme hakkındaki NumPy belgelerinde ayrıntılı olarak ele alınmıştır .

Oh, ve asıl sorunuz hakkında neredeyse unutuyordum: Birden fazla listeyle dizin oluşturmanın beklendiği gibi çalışmasını sağlamak için:

x[[[1],[3]],[1,3]]

Bunun nedeni, dizin dizilerinin ortak bir şekle yayınlanmasıdır . Tabii ki, bu özel örnek için, temel dilimleme ile de yapabilirsiniz:

x[1::2, 1::2]

Bir "slcie-view" nesnesi olan dizinin orijinal diziyle yeniden eşleşmesi için dizileri alt sınıflara ayırmak mümkün olmalıdır. Muhtemelen OP'nin ihtiyaçlarını karşılayabilir
jsbueno

@jsbueno: bu Python kodu için işe yarayacak ancak Scipy / Numpy'nin sardığı C / Fortran rutinleri için çalışmayacak. Bu sarılmış rutinler Numpy'nin gücünün yattığı yerdir.
Dat Chu

Soo .. x [[[1], [3]], [1,3]] ve x [[1,3],:] [:, [1,3]] arasındaki fark nedir? Yani kullanımı diğerinden daha iyi bir varyant var mı?
levesque

1
@JC: x[[[1],[3]],[1,3]]yalnızca bir yeni dizi oluşturur, x[[1,3],:][:,[1,3]]iki kez kopyalanır, bu yüzden ilkini kullanın.
Sven Marnach

@JC: Veya Justin'in cevabındaki yöntemi kullanın.
Sven Marnach

13

Bunun x[[1,3]][:,[1,3]]neredeyse okunabilir olduğunu düşünmüyorum . Niyetinizde daha net olmak istiyorsanız, şunları yapabilirsiniz:

a[[1,3],:][:,[1,3]]

Ben dilimleme konusunda uzman değilim ama tipik olarak, bir diziye dilimlemeye çalışırsanız ve değerler sürekli ise, adım değerinin değiştiği bir görünüme geri dönersiniz.

Örneğin, 33 ve 34 girişlerinizde, 2x2 dizisi elde etmenize rağmen adım 4'tür. Bu nedenle, bir sonraki satırı dizine eklediğinizde, işaretçi bellekte doğru konuma hareket eder.

Açıkçası, bu mekanizma bir dizi indeks durumunda iyi bir performans sergilemez. Bu nedenle, numpy kopyasını yapmak zorundadır. Sonuçta, diğer birçok matris matematik fonksiyonu boyut, adım ve sürekli bellek tahsisine dayanır.


10

Diğer tüm satırları ve diğer sütunları atlamak istiyorsanız, temel dilimleme ile yapabilirsiniz:

In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]: 
array([[ 5,  7],
       [13, 15]])

Bu, dizinizin bir kopyasını değil bir görünüm döndürür.

In [51]: y=x[1:4:2,1:4:2]

In [52]: y[0,0]=100

In [53]: x   # <---- Notice x[1,1] has changed
Out[53]: 
array([[  0,   1,   2,   3],
       [  4, 100,   6,   7],
       [  8,   9,  10,  11],
       [ 12,  13,  14,  15]])

while z=x[(1,3),:][:,(1,3)]gelişmiş dizinleme kullanır ve böylece bir kopyasını döndürür:

In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]

In [60]: z
Out[60]: 
array([[ 5,  7],
       [13, 15]])

In [61]: z[0,0]=0

Not xdeğişmez:

In [62]: x
Out[62]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

İsteğe bağlı satırları ve sütunları seçmek isterseniz, temel dilimleme özelliğini kullanamazsınız. Sıralar ve x[rows,:][:,columns]nerede oldukları gibi gelişmiş bir dizin kullanmanız gerekir . Bu elbette size orijinal dizinizin bir görünümünü değil bir kopyasını verecektir. Bu, beklendiği gibi, çünkü bir numpy dizisi (sabit adımlarla) bitişik bellek kullanıyor ve rastgele satırlar ve sütunlarla bir görünüm oluşturmanın hiçbir yolu olmayacaktı (çünkü bu sabit olmayan adımlar gerektirecektir).rowscolumns


5

Numpy ile, dizinin her bileşeni için bir dilim iletebilirsiniz; böylece x[0:2,0:2]yukarıdaki örnek işe yarar.

Sütunları veya satırları eşit olarak atlamak istiyorsanız, üç bileşene sahip dilimleri geçirebilirsiniz (örn. Başlat, durdur, adım).

Yine, yukarıdaki örnek için:

>>> x[1:4:2, 1:4:2]
array([[ 5,  7],
       [13, 15]])

Temel olarak: birinci boyutta, dizin 1'de başlayarak, dizin 4'e eşit veya daha büyük olduğunda durun ve her geçişte dizine 2 ekleyin. İkinci boyut için de aynı şey geçerli. Tekrar: Bu sadece sabit adımlar için geçerlidir.

Dahili olarak oldukça farklı bir şey yapmanız gereken sözdizimi - x[[1,3]][:,[1,3]]aslında ne yaparsa, orijinal diziden yalnızca 1 ve 3 satırlarını içeren yeni bir dizi oluşturmak ( x[[1,3]]parça ile yapılır ) ve daha sonra da - üçüncü bir dizi oluşturmak - yalnızca önceki dizinin 1. ve 3. sütunları.


1
Bu çözüm, ayıklamaya çalıştığım satırlara / sütunlara özgü olduğu için çalışmıyor. Bir 50x50 matrisinde de aynı şeyi düşünün, 5,11,12,32,39,45 satırlarını / sütunlarını ayıklamak istediğimde, bunu basit dilimlerle yapmanın bir yolu yok. Sorumda net olmasaydım üzgünüm.
levesque


0

Bunun ne kadar verimli olduğundan emin değilim ama her iki eksende dilimlemek için range () kullanabilirsiniz

 x=np.arange(16).reshape((4,4))
 x[range(1,3), :][:,range(1,3)] 
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.