Pengindeksan aneh menggunakan numpy

27

Saya memiliki variabel, x, yang berbentuk (2,2,50,100).

Saya juga memiliki array, y, yang sama dengan np.array ([0,10,20]). Suatu hal yang aneh terjadi ketika saya mengindeks x [0,:,:, y].

x = np.full((2,2,50,100),np.nan)
y = np.array([0,10,20])
print(x.shape)
(2,2,50,100)
print(x[:,:,:,y].shape)
(2,2,50,3)
print(x[0,:,:,:].shape)
(2,50,100)
print(x[0,:,:,y].shape)
(3,2,50)

Mengapa output terakhir (3,2,50) dan tidak (2,50,3)?

Paul Scotti
sumber
Saya baru ke numpy, jadi saya tidak punya jawaban untuk pertanyaan Anda. Untuk menyelidiki ini lebih lanjut, saya sarankan mencari contoh yang lebih kecil yang hanya 2D atau 3D dan hanya seperti paling banyak 10 elemen pada sumbu apa pun.
Code-Apprentice

Jawaban:

21

Ini adalah bagaimana numpy menggunakan pengindeksan tingkat lanjut untuk menyiarkan bentuk array. Saat Anda melewati a 0untuk indeks pertama, dan yuntuk indeks terakhir, numpy akan menyiarkan 0bentuk yang sama dengan y. Kesetaraan berikut ini berlaku: x[0,:,:,y] == x[(0, 0, 0),:,:,y]. di sini adalah sebuah contoh

import numpy as np

x = np.arange(120).reshape(2,3,4,5)
y = np.array([0,2,4])

np.equal(x[0,:,:,y], x[(0, 0, 0),:,:,y]).all()
# returns:
True

Sekarang, karena Anda secara efektif mengirimkan dua set indeks, Anda menggunakan API pengindeksan lanjutan untuk membentuk (dalam hal ini) pasangan indeks.

x[(0, 0, 0),:,:,y])

# equivalent to
[
  x[0,:,:,y[0]], 
  x[0,:,:,y[1]], 
  x[0,:,:,y[2]]
]

# equivalent to
rows = np.array([0, 0, 0])
cols = y
x[rows,:,:,cols]

# equivalent to
[
  x[r,:,:,c] for r, c in zip(rows, columns)
]

Yang memiliki dimensi pertama yang sama dengan panjang y. Ini yang Anda lihat.

Sebagai contoh, lihat sebuah array dengan 4 dimensi yang dijelaskan dalam potongan berikutnya:

x = np.arange(120).reshape(2,3,4,5)
y = np.array([0,2,4])

# x looks like:
array([[[[  0,   1,   2,   3,   4],    -+      =+
         [  5,   6,   7,   8,   9],     Sheet1  |
         [ 10,  11,  12,  13,  14],     |       |
         [ 15,  16,  17,  18,  19]],   -+       |
                                                Workbook1
        [[ 20,  21,  22,  23,  24],    -+       |
         [ 25,  26,  27,  28,  29],     Sheet2  |
         [ 30,  31,  32,  33,  34],     |       |
         [ 35,  36,  37,  38,  39]],   -+       |
                                                |
        [[ 40,  41,  42,  43,  44],    -+       |
         [ 45,  46,  47,  48,  49],     Sheet3  |
         [ 50,  51,  52,  53,  54],     |       |
         [ 55,  56,  57,  58,  59]]],  -+      =+


       [[[ 60,  61,  62,  63,  64],
         [ 65,  66,  67,  68,  69],
         [ 70,  71,  72,  73,  74],
         [ 75,  76,  77,  78,  79]],

        [[ 80,  81,  82,  83,  84],
         [ 85,  86,  87,  88,  89],
         [ 90,  91,  92,  93,  94],
         [ 95,  96,  97,  98,  99]],

        [[100, 101, 102, 103, 104],
         [105, 106, 107, 108, 109],
         [110, 111, 112, 113, 114],
         [115, 116, 117, 118, 119]]]])

x memiliki bentuk sekuensial yang sangat mudah dipahami yang sekarang dapat kita gunakan untuk menunjukkan apa yang terjadi ...

Dimensi pertama seperti memiliki 2 Buku Kerja Excel, dimensi kedua seperti memiliki 3 lembar di setiap buku kerja, dimensi ketiga seperti memiliki 4 baris per lembar, dan dimensi terakhir adalah 5 nilai untuk setiap baris (atau kolom per lembar).

Melihat seperti ini, meminta x[0,:,:,0], adalah pepatah: "di buku kerja pertama, untuk setiap lembar, untuk setiap baris, beri saya nilai / kolom pertama."

x[0,:,:,y[0]]
# returns:
array([[ 0,  5, 10, 15],
       [20, 25, 30, 35],
       [40, 45, 50, 55]])

# this is in the same as the first element in:
x[(0,0,0),:,:,y]

Tetapi sekarang dengan pengindeksan tingkat lanjut, kita dapat menganggapnya x[(0,0,0),:,:,y]sebagai "di buku kerja pertama, untuk setiap lembar, untuk setiap baris, beri saya nilai yth / kolom. Ok, sekarang lakukan untuk setiap nilai y"

x[(0,0,0),:,:,y]
# returns:
array([[[ 0,  5, 10, 15],
        [20, 25, 30, 35],
        [40, 45, 50, 55]],

       [[ 2,  7, 12, 17],
        [22, 27, 32, 37],
        [42, 47, 52, 57]],

       [[ 4,  9, 14, 19],
        [24, 29, 34, 39],
        [44, 49, 54, 59]]])

Di mana menjadi gila adalah bahwa numpy akan disiarkan untuk mencocokkan dimensi luar array indeks. Jadi jika Anda ingin melakukan operasi yang sama seperti di atas, tetapi untuk KEDUA "buku kerja Excel", Anda tidak perlu mengulang dan menyatukan. Anda bisa meneruskan array ke dimensi pertama, tetapi itu HARUS memiliki bentuk yang kompatibel.

Melewati bilangan bulat disiarkan ke y.shape == (3,). Jika Anda ingin melewatkan array sebagai indeks pertama, hanya dimensi terakhir dari array yang harus kompatibel dengannya y.shape. Yaitu, dimensi terakhir dari indeks pertama harus 3 atau 1.

ix = np.array([[0], [1]])
x[ix,:,:,y].shape
# each row of ix is broadcast to length 3:
(2, 3, 3, 4)

ix = np.array([[0,0,0], [1,1,1]])
x[ix,:,:,y].shape
# this is identical to above:
(2, 3, 3, 4)

ix = np.array([[0], [1], [0], [1], [0]])
x[ix,:,:,y].shape
# ix is broadcast so each row of ix has 3 columns, the length of y
(5, 3, 3, 4)

Temukan penjelasan singkat dalam dokumen: https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing


Edit:

Dari pertanyaan awal, untuk mendapatkan satu-langganan berlangganan yang Anda inginkan, Anda dapat menggunakan x[0][:,:,y]:

x[0][:,:,y].shape
# returns
(2, 50, 3)

Namun, jika Anda mencoba untuk menetapkan untuk berlangganan tersebut, Anda harus sangat berhati-hati bahwa Anda sedang melihat tampilan memori bersama dari array asli. Kalau tidak, tugas tidak akan ke array asli, tetapi salinan.

Memori bersama hanya terjadi ketika Anda menggunakan integer atau slice untuk subset array Anda, yaitu x[:,0:3,:,:]atau x[0,:,:,1:-1].

np.shares_memory(x, x[0])
# returns:
True

np.shares_memory(x, x[:,:,:,y])
# returns:
False

Baik dalam pertanyaan awal Anda maupun contoh saya ybukanlah int atau slice, maka akan selalu berakhir dengan menetapkan salinan asli.

TAPI! Karena larik Anda untuk ydapat diekspresikan sebagai slice, Anda BISA benar-benar mendapatkan tampilan larik array Anda melalui:

x[0,:,:,0:21:10].shape
# returns:
(2, 50, 3)

np.shares_memory(x, x[0,:,:,0:21:10])
# returns:
True

# actually assigns to the original array
x[0,:,:,0:21:10] = 100

Di sini kita menggunakan irisan 0:21:10untuk mengambil setiap indeks yang akan masuk range(0,21,10). Kita harus menggunakan 21dan bukan 20karena stop-point dikecualikan dari slice, sama seperti dalam rangefungsi.

Jadi pada dasarnya, jika Anda bisa membuat slice yang sesuai dengan kriteria berlangganan Anda, Anda bisa melakukan penugasan.

James
sumber
4

Ini disebut combining advanced and basic indexing. Dalam combining advanced and basic indexing, numpy melakukan pengindeksan di pengindeksan lanjutan pertama dan subruang / menggabungkan hasilnya ke dimensi pengindeksan dasar.

Contoh dari dokumen:

Biarkan x.shape menjadi (10,20,30,40,50) dan anggaplah ind_1 dan ind_2 dapat disiarkan ke bentuk (2,3,4). Kemudian x [:, ind_1, ind_2] memiliki bentuk (10,2,3,4,40,50) karena subruang berbentuk (20,30) dari X telah diganti dengan subruang (2,3,4) dari indeks. Namun, x [:, ind_1,:, ind_2] memiliki bentuk (2,3,4,10,30,50) karena tidak ada tempat yang jelas untuk menjatuhkan subruang pengindeksan, sehingga ia ditempelkan ke awal . Selalu dimungkinkan untuk menggunakan .transpose () untuk memindahkan ruang bagian mana saja yang diinginkan. Perhatikan bahwa contoh ini tidak dapat direplikasi menggunakan take.

jadi, aktif x[0,:,:,y], 0dan ysedang maju pengindeksan. Mereka disiarkan bersama untuk menghasilkan dimensi (3,).

In [239]: np.broadcast(0,y).shape
Out[239]: (3,)

Ini berlaku (3,)untuk awal dimensi 2 dan 3 untuk membuat(3, 2, 50)

Untuk melihat bahwa 1 dan dimensi terakhir benar-benar penyiaran bersama-sama, Anda dapat mencoba perubahan 0untuk [0,1]melihat kesalahan penyiaran

print(x[[0,1],:,:,y])

Output:
IndexError                                Traceback (most recent call last)
<ipython-input-232-5d10156346f5> in <module>
----> 1 x[[0,1],:,:,y]

IndexError: shape mismatch: indexing arrays could not be broadcast together with
 shapes (2,) (3,)
Andy L.
sumber