Многомерное индексирование с Numpy - PullRequest
1 голос
/ 04 февраля 2020

Я использую трехмерный массив, который определяется следующим образом:

x = np.zeros((dim1, dim2, dim3), dtype=np.float32)

После вставки некоторых данных мне нужно применять функцию, только если значения в указанных столбцах c по-прежнему равны нулю. Интересующие меня столбцы выбираются этим массивом, содержащим правильные индексы

scale_idx = np.array([0,1,3])

, поэтому я пытаюсь использовать индексирование для выбора этих строк и столбцов.

Сначала я попытался сделать это, используя логическую маску для первых двух измерений, используя массив для третьего:

x[x[:,:,scale_idx].any(axis =2)] ,scale_idx]

, но я получаю эту ошибку:

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

Если Я изменяю последний индекс на : Я получаю все интересующие меня строки, но я получаю все возможные столбцы, я ожидал, что последний массив будет действовать как индексатор, как объяснено в https://docs.scipy.org/doc/numpy/user/basics.indexing.html .

x[x[:,:,scale_idx].any(axis =2)]

Мои scale_idx должны интерпретироваться как индексаторы столбцов, но фактически интерпретируются как индексы строк, поэтому, поскольку только 2 строки соответствуют условию, но у меня есть 3 индекса, я получаю IndexError.

Я нашел обходной путь для этого, используя

x[x[:,:,scale_idx].any(axis =2)][:,:,scale_idx]

, но это довольно уродливо и, поскольку это фрагмент, я не могу изменить исходный массив.

Никто готовы объяснить мне, что я делаю не так?

РЕДАКТИРОВАТЬ: Благодаря @hpaulj мне удалось выделить нужные мне ячейки, после чего я создал матрицу с той же формой выбранных значений и присвоил значения замаскированным ячейкам, чтобы к моему удивлению, новые значения - это не те, которые я только что установил, а некоторые случайные целые числа, которые я не могу понять, откуда они пришли. Код для воспроизведения:

scale_idx = np.array([0,3,1])
b = x[:,:,scale_idx].any(axis =2)
I, J = np.nonzero(b)
x[I[:,None], J[:,None], scale_idx] #this selects the correct cells
>>>
array([[ 50,  50,  50],
     [100, 100, 100],
     [100, 100, 100]])
scaler.transform(x[I[:,None], J[:,None], scale_idx]) #sklearn standard scaler, returns a matrix with the scaled values
>>>
array([[-0.50600345, -0.5445559 , -1.2957878 ],
     [-0.50600345, -0.25915199, -1.22266904],
     [-0.50600345, -0.25915199, -1.22266904]]) 
x[I[:,None], J[:,None], scale_idx] = scaler.transform(x[I[:,None], J[:,None], scale_idx]) #assign the new values to the selected cells
x[I[:,None], J[:,None], scale_idx] #check the new values

array([[0, 2, 0],
     [0, 6, 2],
     [0, 6, 2]])

Почему новые значения отличаются от того, что я ожидаю?

1 Ответ

2 голосов
/ 04 февраля 2020

Давайте возьмем пример 3d логической маски из indexing документов:

In [135]: x = np.arange(30).reshape(2,3,5) 
     ...: b = np.array([[True, True, False], [False, True, True]])                             
In [136]: x                                                                                    
Out[136]: 
array([[[ 0,  1,  2,  3,  4],
        [ 5,  6,  7,  8,  9],
        [10, 11, 12, 13, 14]],

       [[15, 16, 17, 18, 19],
        [20, 21, 22, 23, 24],
        [25, 26, 27, 28, 29]]])
In [137]: b                                                                                    
Out[137]: 
array([[ True,  True, False],
       [False,  True,  True]])
In [138]: x[b]                                                                                 
Out[138]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [20, 21, 22, 23, 24],
       [25, 26, 27, 28, 29]])

Это 2d массив. Маска b выделяет элементы из первых двух измерений. Значения False заставляют пропускать строки [10 ...] и [15 ...].

Мы можем нарезать последнее измерение:

In [139]: x[b,:3]                                                                              
Out[139]: 
array([[ 0,  1,  2],
       [ 5,  6,  7],
       [20, 21, 22],
       [25, 26, 27]])

, но Индекс списка выдаст ошибку (если длина не 4):

In [140]: x[b,[0,1,2]]                                                                         
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-140-7f1dbec100f2> in <module>
----> 1 x[b,[0,1,2]]

IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (4,) (4,) (3,) 

Причина в том, что логическая маска эффективно преобразуется в индекс с массивами np.where:

In [141]: np.nonzero(b)                                                                        
Out[141]: (array([0, 0, 1, 1]), array([0, 1, 1, 2]))

nonzero найдено 4 ненулевых элемента. Тогда индексирование x[b] будет:

In [143]: x[[0,0,1,1],[0,1,1,2],:]                                                             
Out[143]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [20, 21, 22, 23, 24],
       [25, 26, 27, 28, 29]])

https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#boolean -индексирование массива

Тогда несоответствие формы становится более очевидным:

In [144]: x[[0,0,1,1],[0,1,1,2],[1,2,3]]                                                       
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-144-1efd76049cb0> in <module>
----> 1 x[[0,0,1,1],[0,1,1,2],[1,2,3]]

IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (4,) (4,) (3,) 

Если списки совпадают по размеру, индексация запускается, но выдает «диагональ», а не блок:

In [145]: x[[0,0,1,1],[0,1,1,2],[1,2,3,4]]                                                     
Out[145]: array([ 1,  7, 23, 29])

Как вы обнаружили, двухэтапная индексация работает - но не для значений настройки

In [146]: x[[0,0,1,1],[0,1,1,2]][:,[1,2,3]]                                                    
Out[146]: 
array([[ 1,  2,  3],
       [ 6,  7,  8],
       [21, 22, 23],
       [26, 27, 28]])

Мы можем получить блок «транспонированием» последнего списка индексов:

In [147]: x[[0,0,1,1],[0,1,1,2],[[1],[2],[3]]]                                                 
Out[147]: 
array([[ 1,  6, 21, 26],
       [ 2,  7, 22, 27],
       [ 3,  8, 23, 28]])

Хорошо, это транспонирование. Мы могли бы применить транспонирование к нему. Или мы могли бы сначала транспонировать массивы b:

In [148]: I,J=np.nonzero(b)                                                                    
In [149]: x[I[:,None], J[:,None], [1,2,3]]                                                     
Out[149]: 
array([[ 1,  2,  3],
       [ 6,  7,  8],
       [21, 22, 23],
       [26, 27, 28]])

И это работает для установки

In [150]: x[I[:,None], J[:,None], [1,2,3]]=0                                                   
In [151]: x                                                                                    
Out[151]: 
array([[[ 0,  0,  0,  0,  4],
        [ 5,  0,  0,  0,  9],
        [10, 11, 12, 13, 14]],

       [[15, 16, 17, 18, 19],
        [20,  0,  0,  0, 24],
        [25,  0,  0,  0, 29]]])

Это длинный ответ. У меня было общее представление о том, что происходит, но мне нужно было проработать детали. Кроме того, вам нужно понять, что происходит.

...