NumPy, bir dizin listesi kullanarak satır başına belirli sütun dizinini seçme


94

Bir NumPy matrisinin her satırı için belirli sütunları seçmekte zorlanıyorum.

Diyelim ki aşağıdaki matrise sahip olduğumu varsayalım X:

[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

Ayrıca listher satır için arayacağım bir sütun dizinim var Y:

[1, 0, 2]

Değerleri almam gerekiyor:

[2]
[4]
[9]

Bunun yerine, bir ait listdizinler ile Y, aynı zamanda da aynı şekle sahip olan bir matris üretebilir Xher sütunda olduğu bool/ intbu gerekli sütun olup olmadığını gösteren, aralık 0-1 değerinde.

[0, 1, 0]
[1, 0, 0]
[0, 0, 1]

Bunun dizi üzerinde yineleyerek ve ihtiyacım olan sütun değerlerini seçerek yapılabileceğini biliyorum. Ancak, bu sık sık büyük veri dizileri üzerinde yürütülecektir ve bu yüzden olabildiğince hızlı çalışması gerekir.

Bu yüzden daha iyi bir çözüm olup olmadığını merak ediyordum?


Cevap senin için daha mı iyi? stackoverflow.com/a/17081678/5046896
GoingMyWay

Yanıtlar:


104

Bir boole diziniz varsa, buna göre doğrudan seçim yapabilirsiniz:

>>> a = np.array([True, True, True, False, False])
>>> b = np.array([1,2,3,4,5])
>>> b[a]
array([1, 2, 3])

İlk örneğinizle devam etmek için şunları yapabilirsiniz:

>>> a = np.array([[1,2,3], [4,5,6], [7,8,9]])
>>> b = np.array([[False,True,False],[True,False,False],[False,False,True]])
>>> a[b]
array([2, 4, 9])

Ayrıca arange, boole dizinizi nasıl oluşturduğunuza ve kodunuzun YMMV'ye neye benzediğine bağlı olarak, buna bir de ekleyebilir ve doğrudan seçim yapabilirsiniz.

>>> a = np.array([[1,2,3], [4,5,6], [7,8,9]])
>>> a[np.arange(len(a)), [1,0,2]]
array([2, 4, 9])

Umarım yardımcı olur, başka sorunuz olursa bana bildirin.


13
Kullanan örnek için +1 arange. Bu, birden çok matristen farklı blokları almak için özellikle yararlı oldu (yani temelde bu örneğin 3B durumu)
Griddo

1
Merhaba, neden arangeyerine kullanmak zorunda olduğumuzu açıklar :mısınız? Senin yolunun işe yaradığını ve benimkinin olmadığını biliyorum, ama nedenini anlamak isterim.
marcotama

@tamzord, vanilla python listesi değil, numpy bir dizi olduğu için :sözdizimi aynı şekilde çalışmıyor.
Slater Victoroff

1
@SlaterTyranus, yanıtladığınız için teşekkürler. Anladığım kadarıyla, biraz okuduktan sonra, :gelişmiş indeksleme ile karıştırmanın şu anlama geldiğidir: "boyunca her alt alan :için, verilen gelişmiş indekslemeyi uygulayın". Anladığım doğru mu?
marcotama

@tamzord "alt uzay" ile ne demek istediğinizi açıklayın
Slater Victoroff

36

Bunun gibi bir şey yapabilirsiniz:

In [7]: a = np.array([[1, 2, 3],
   ...: [4, 5, 6],
   ...: [7, 8, 9]])

In [8]: lst = [1, 0, 2]

In [9]: a[np.arange(len(a)), lst]
Out[9]: array([2, 4, 9])

Çok boyutlu dizilerin indekslenmesi hakkında daha fazla bilgi: http://docs.scipy.org/doc/numpy/user/basics.indexing.html#indexing-multi-dimensional-arrays


2
neden basitçe ':' veya aralık yerine aralığa ihtiyaç duyulduğunu anlamaya çalışarak.
MadmanLee

@MadmanLee Merhaba, kullanımı sonuçların :birden çok len(a)kez çıktı alınmasını sağlar, bunun yerine her satırın dizininin beklenen sonuçları yazdıracağını belirtir.
GoingMyWay

1
Bence bu, bu sorunu çözmenin tam olarak doğru ve zarif yolu.
GoingMyWay

6

Basit bir yol şöyle görünebilir:

In [1]: a = np.array([[1, 2, 3],
   ...: [4, 5, 6],
   ...: [7, 8, 9]])

In [2]: y = [1, 0, 2]  #list of indices we want to select from matrix 'a'

range(a.shape[0]) dönecek array([0, 1, 2])

In [3]: a[range(a.shape[0]), y] #we're selecting y indices from every row
Out[3]: array([2, 4, 9])

1
Lütfen açıklama eklemeyi düşünün.
souki

@souki Şimdi açıklama ekledim. Teşekkürler
Dhaval Mayatra

6

Son numpysürümler , bu indekslemeyi temiz bir şekilde yapan bir take_along_axis(ve put_along_axis) ekledi .

In [101]: a = np.arange(1,10).reshape(3,3)                                                             
In [102]: b = np.array([1,0,2])                                                                        
In [103]: np.take_along_axis(a, b[:,None], axis=1)                                                     
Out[103]: 
array([[2],
       [4],
       [9]])

Aşağıdakilerle aynı şekilde çalışır:

In [104]: a[np.arange(3), b]                                                                           
Out[104]: array([2, 4, 9])

ancak farklı eksen işleme ile. Özellikle sonuçlarını uygulamak hedefleniyor argsortve argmax.


3

Yineleyici kullanarak yapabilirsiniz. Bunun gibi:

np.fromiter((row[index] for row, index in zip(X, Y)), dtype=int)

Zaman:

N = 1000
X = np.zeros(shape=(N, N))
Y = np.arange(N)

#@Aशwini चhaudhary
%timeit X[np.arange(len(X)), Y]
10000 loops, best of 3: 30.7 us per loop

#mine
%timeit np.fromiter((row[index] for row, index in zip(X, Y)), dtype=int)
1000 loops, best of 3: 1.15 ms per loop

#mine
%timeit np.diag(X.T[Y])
10 loops, best of 3: 20.8 ms per loop

1
OP, büyük dizilerde hızlı çalışması gerektiğinden bahsetti , bu nedenle kıyaslamalarınız pek temsilci değil. Son yönteminizin (çok) daha büyük diziler için nasıl performans gösterdiğini merak ediyorum!

@moarningsun: Güncellendi. np.diag(X.T[Y])çok yavaş ... Ama np.diag(X.T)çok hızlı (10us). Neden bilmiyorum
Kei Minagawa

0

Bir başka akıllıca yol, önce diziyi transpoze etmek ve daha sonra indekslemektir. Son olarak, köşegeni alın, her zaman doğru cevaptır.

X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
Y = np.array([1, 0, 2, 2])

np.diag(X.T[Y])

Adım adım:

Orijinal diziler:

>>> X
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])

>>> Y
array([1, 0, 2, 2])

Doğru dizine eklemeyi mümkün kılmak için transpoze edin.

>>> X.T
array([[ 1,  4,  7, 10],
       [ 2,  5,  8, 11],
       [ 3,  6,  9, 12]])

Y sırasına göre satırları alın.

>>> X.T[Y]
array([[ 2,  5,  8, 11],
       [ 1,  4,  7, 10],
       [ 3,  6,  9, 12],
       [ 3,  6,  9, 12]])

Köşegen şimdi netleşmelidir.

>>> np.diag(X.T[Y])
array([ 2,  4,  9, 12]

1
Bu teknik olarak işe yarıyor ve çok şık görünüyor. Ancak, büyük dizilerle uğraşırken bu yaklaşımın tamamen patladığını görüyorum. Benim durumumda NumPy 30GB takas aldı ve SSD'mi doldurdu. Bunun yerine gelişmiş indeksleme yaklaşımını kullanmanızı tavsiye ederim.
5nefari
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.