Странное индексирование с использованием numpy - PullRequest
27 голосов
/ 27 февраля 2020

У меня есть переменная 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)?

Ответы [ 2 ]

21 голосов
/ 27 февраля 2020

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

Первое измерение похоже на наличие двух книг 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] как "в первой книге, для каждого листа, для каждой строки, дайте мне y th значение / столбец. Хорошо, Теперь сделайте это для каждого значения 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», вам не нужно l oop и объединять. Вы можете просто передать массив в первое измерение, но он ДОЛЖЕН иметь совместимую форму.

Передача целого числа транслируется в 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-basi c -индексирование


Редактировать:

Исходя из исходного вопроса, чтобы получить одну строчку желаемого сублицензии, вы можете использовать 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, ни слайсом, поэтому всегда будет назначать копию оригинала.

НО! Поскольку ваш массив для y может быть выражен в виде слайса, вы CAN фактически получаете назначаемое представление вашего массива с помощью:

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 голосов
/ 27 февраля 2020

Это называется combining advanced and basic indexing. В combining advanced and basic indexing, numpy сначала выполните индексацию в расширенной индексации и подпространство / объедините результат с измерением basi c indexing.

Пример из документации:

Пусть 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,) привязывается к началу 2-го и 3-го измерения, чтобы сделать (3, 2, 50)

Чтобы увидеть, что 1-е и последнее измерение действительно передаются вместе, вы можете попробовать изменить 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,)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...