Дивна індексація за допомогою numpy


27

У мене є змінна, x, що має форму (2,2,50,100).

У мене також є масив y, який дорівнює np.array ([0,10,20]). Дивна річ трапляється, коли я індексую 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)

Чому останній вихід (3,2,50), а ні (2,50,3)?


Я новачок в нуме, тому я не маю відповіді на ваше запитання. Для подальшого дослідження цього я пропоную знайти менший приклад, який є лише 2D або 3D і є як би щонайменше 10 елементів на будь-якій осі.
Код-учень

Відповіді:


21

Ось як numpy використовує розширену індексацію для трансляції фігур масиву. Коли ви передасте 0для першого індексу та yдля останнього індексу, numpy передасть трансляцію 0такої ж форми, що і y. Еквівалентність має місце x[0,:,:,y] == x[(0, 0, 0),:,:,y]. ось приклад

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

Тепер, оскільки ви ефективно передаєте два набори індексів, ви використовуєте розширений API індексування для формування (в даному випадку) пар індексів.

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

Який має перший вимір, такий самий, як і довжина y. Це те, що ти бачиш.

Як приклад, подивіться на масив із 4-ма вимірами, які описані в наступному фрагменті:

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 має дійсно просту для розуміння послідовну форму, яку ми зараз можемо використовувати, щоб показати, що відбувається ...

Перший вимір - це те, як 2 робочі книги Excel, другий - як 3 аркуші в кожній робочій книжці, третій - як мати 4 рядки на аркуші, а останній - 5 значень для кожного рядка (або стовпців на аркуші).

Дивлячись на це так, запитуючи x[0,:,:,0], є приказка: "в першому робочому зошиті, для кожного аркуша, для кожного рядка, дайте мені перше значення / стовпець".

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]

Але тепер, за допомогою розширеної індексації, ми можемо вважати, x[(0,0,0),:,:,y]як "у першій робочій книжці, для кожного аркуша, для кожного рядка, дайте мені yth значення / стовпець. Добре, тепер зробіть це для кожного значення 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]]])

Де божеволіє, що numpy транслюватиметься відповідно до зовнішніх розмірів масиву індексів. Отже, якщо ви хочете виконати ту саму операцію, що і вище, але для БОТУ «Робочі книги Excel», вам не доведеться циклічно і з'єднуватися. Ви можете просто передати масив до першого виміру, але він ОБОВ'ЯЗКОВО мати сумісну форму.

Передача цілого числа отримує трансляцію до y.shape == (3,). Якщо ви хочете передати масив як перший індекс, повинен бути сумісний лише останній вимір масиву y.shape. Тобто останній вимір першого індексу повинен бути або 3, або 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)

Знайдено коротке пояснення в документах: https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing


Редагувати:

З оригінального запитання, щоб отримати однолінійку потрібного підписання, ви можете використовувати x[0][:,:,y]:

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

Однак, якщо ви намагаєтеся призначити ці підпункти, ви повинні бути дуже обережними, щоб ви дивились на спільну пам’ять з оригінальним масивом. Інакше призначення буде не оригінальному масиву, а копії.

Спільна пам'ять виникає лише тоді, коли ви використовуєте ціле число або фрагмент для підмноження масиву, тобто x[:,0:3,:,:]або x[0,:,:,1:-1].

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

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

І в вашому оригінальному запитанні, і в моєму прикладі yне є ні int, ні фрагмент, тому завжди буде присвоєно копію оригіналу.

АЛЕ! Оскільки ваш масив for yможе бути виражений у вигляді фрагмента, ви МОЖЛИВО отримати можливість перегляду масиву через:

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

Тут ми використовуємо фрагмент, 0:21:10щоб схопити кожен індекс, який би знаходився range(0,21,10). Ми повинні використовувати, 21а не 20тому, що точка зупинки виключається з фрагмента, як і у rangeфункції.

Таким чином, якщо ви можете створити фрагмент, який відповідає вашим критеріям сублітування, ви можете виконати завдання.


4

Це називається combining advanced and basic indexing. У combining advanced and basic indexing, numpy, зробіть індексацію в розширеній індексації спочатку та підпростір / об'єднайте результат у вимір базової індексації.

Приклад із документів:

Нехай x.shape буде (10,20,30,40,50) і припустимо, що ind_1 та ind_2 можуть транслюватися у форму (2,3,4). Тоді x [:, ind_1, ind_2] має форму (10,2,3,4,40,50), тому що (20,30) -образний підпростір з X замінено на (2,3,4) підпростір від індекси. Однак x [:, ind_1,:, ind_2] має форму (2,3,4,10,30,50), оскільки немає однозначного місця для падіння в підпростір індексації, таким чином, він прикріплений до початку . Завжди можна використовувати .transpose () для переміщення підпростору куди завгодно. Зауважте, що цей приклад неможливо повторити за допомогою take.

так, на x[0,:,:,y], 0іy наперед здійснюють попередню індексацію. Вони транслюються разом, щоб отримати вимір (3,).

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

Це (3,)стосується початку другого та третього вимірів(3, 2, 50)

Для того, щоб побачити , що перший і останній аспект дійсно мовлення разом, ви можете спробувати зміни 0в , [0,1]щоб побачити помилку мовлення

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,)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.