Изменение подмассивов в 3D-массивах с использованием значений 2D-массива с использованием numpy - PullRequest
1 голос
/ 09 июля 2020

Я работаю над кодом, и в голове просто возникает вопрос. Итак, в основном у меня есть массив 3D numpy с формой (2, 5, 5), а также массив 2D numpy с формой (2, 4) (это просто пример, массивы могут быть намного больше). Мне нужно заменить значения 1 в подмассивах 3D-массива (срез [:, 2:, 2:]) значениями в моем 2D-массиве. Я думал о том, чтобы получить индекс из значений, которые я хочу изменить (те) в 3D-массиве, а затем использовать для l oop в 2D-массиве, чтобы перебирать значения, но я не уверен, что это эффективный способ и также я получаю сообщение об ошибке.

import numpy as np

arr = np.array([[[0.,  1., 43., 25., 21.],
                 [0.,  0.,  0.,  0.,  0.],
                 [0., 43.,  0.,  1.,  0.], 
                 [0., 43.,  1.,  0.,  1.],
                 [0., 45.,  0.,  1.,  0.]],

                [[0.,  1., 38., 29., 46.],
                 [0.,  0.,  0.,  0.,  0.],
                 [0., 32.,  0.,  0.,  1.],
                 [0., 26.,  0.,  0.,  1.],
                 [0., 30.,  1.,  1.,  0.]]])

values = [[2, 3, 1, 4],
          [4, 1, 5, 9]]

indexes = np.argwhere(newarr[:, 2:, 2:] == 1) + [0, 2, 2]

# indexes = [[0 2 3]
#            [0 3 2]
#            [0 3 4]
#            [0 4 3]
#            [1 2 4]
#            [1 3 4]
#            [1 4 2]
#            [1 4 3]]

for i in values:
    arr[indexes] == i

#Error
#index 2 is out of bounds for axis 0 with size 2

Мой желаемый результат должен быть

newarr = [[[0.,  1., 43., 25., 21.],
           [0.,  0.,  0.,  0.,  0.],
           [0., 43.,  0.,  2.,  0.], 
           [0., 43.,  3.,  0.,  1.],
           [0., 45.,  0.,  4.,  0.]],

          [[0.,  1., 38., 29., 46.],            
           [0.,  0.,  0.,  0.,  0.],
           [0., 32.,  0.,  0.,  4.],
           [0., 26.,  0.,  0.,  1.],
           [0., 30.,  5.,  9.,  0.]]])

Я думаю, что должно быть более эффективное использование только numpy, но я не могу посмотрите, как это сделать, поэтому любая помощь будет оценена по достоинству, спасибо!

Ответы [ 2 ]

1 голос
/ 09 июля 2020

Две проблемы:

  • используйте np.where вместо argwhere; вам не нужно повторять
  • полученные индексы применяются к срезу, а не к исходному массиву

С вашим массивом:

In [133]: arr = np.array([[[0.,  1., 43., 25., 21.], 
     ...:                  [0.,  0.,  0.,  0.,  0.], 
          ...
     ...:                  [0., 26.,  0.,  0.,  1.], 
     ...:                  [0., 30.,  1.,  1.,  0.]]]) 
     ...:  

срез:

In [136]: subarr = arr[:,2:,2:]                                                                      

, где значения среза равны 1:

In [137]: np.nonzero(subarr==1)                                                                      
Out[137]: 
(array([0, 0, 0, 0, 1, 1, 1, 1]),
 array([0, 1, 1, 2, 0, 1, 2, 2]),
 array([1, 0, 2, 1, 2, 2, 0, 1]))

Это похоже на ваш argwhere, за исключением кортежа массивов, а не смещения.

In [138]: values = [[2, 3, 1, 4], 
     ...:           [4, 1, 5, 9]] 
     ...:                                     

Этот кортеж можно использовать для индексации среза как для выборки, так и для установки:

In [139]: subarr[np.nonzero(subarr==1)]                                                              
Out[139]: array([1., 1., 1., 1., 1., 1., 1., 1.])
In [140]: subarr[np.nonzero(subarr==1)]=np.ravel(values)             

Поскольку subarr является представлением, установка значений в нем также устанавливает значения в arr. Нет необходимости преобразовывать индексы в структуру arr.

In [141]: arr                                                                                        
Out[141]: 
array([[[ 0.,  1., 43., 25., 21.],
        [ 0.,  0.,  0.,  0.,  0.],
        [ 0., 43.,  0.,  2.,  0.],
        [ 0., 43.,  3.,  0.,  1.],
        [ 0., 45.,  0.,  4.,  0.]],

       [[ 0.,  1., 38., 29., 46.],
        [ 0.,  0.,  0.,  0.,  0.],
        [ 0., 32.,  0.,  0.,  4.],
        [ 0., 26.,  0.,  0.,  1.],
        [ 0., 30.,  5.,  9.,  0.]]])

логическое индексирование

Как и другие примечания к ответу, мы можем выбирать элементы с помощью логической маски без nonzero шаг (под обложками индексация похожа, если не идентична).

In [144]: arr = np.array([[[0.,  1., 43., 25., 21.], 
     ...:                  [0.,  0.,  0.,  0.,  0.], 
    ...
     ...:                  [0., 30.,  1.,  1.,  0.]]]) 
     ...:                                                                                            
In [145]: subarr = arr[:,2:,2:]                                                                      
In [146]: subarr[subarr==1]                                                                          
Out[146]: array([1., 1., 1., 1., 1., 1., 1., 1.])

In [148]: subarr[subarr==1] = np.ravel(values)                                                       
In [149]: arr                                                                                        
Out[149]: 
array([[[ 0.,  1., 43., 25., 21.],
        [ 0.,  0.,  0.,  0.,  0.],
        ....
        [ 0., 30.,  5.,  9.,  0.]]])

ближе к вашей попытке

На самом деле ваша итерация могла бы сработать, если бы вы повторили indices:

for i,v in zip(idx,np.ravel(values)):
     arr[tuple(i)] == v

Начиная с моего where кортежа:

In [159]: Out[137]                                                                                   
Out[159]: 
(array([0, 0, 0, 0, 1, 1, 1, 1]),
 array([0, 1, 1, 2, 0, 1, 2, 2]),
 array([1, 0, 2, 1, 2, 2, 0, 1]))
In [160]:                                                                                            

Ваше смещение argwhere:

In [160]: idx = np.transpose(Out[137])+[0,2,2]                                                       
In [161]: idx                                                                                        
Out[161]: 
array([[0, 2, 3],
       [0, 3, 2],
       [0, 3, 4],
       [0, 4, 3],
       [1, 2, 4],
       [1, 3, 4],
       [1, 4, 2],
       [1, 4, 3]])

Используя это итеративно для индексации arr (примечание использование tuple):

In [162]: [arr[tuple(i)] for i in idx]                                                               
Out[162]: [2.0, 3.0, 1.0, 4.0, 4.0, 1.0, 5.0, 9.0]

Итерация непосредственно на values не работает, так как это возвращает 2 списка. Его нужно сплющить / разгладить.

In [163]: for v in values: print(v)                                                                  
[2, 3, 1, 4]
[4, 1, 5, 9]
1 голос
/ 09 июля 2020

Вы можете вырезать arr и использовать фактические двоичные значения, приведенные к bool, чтобы замаскировать массив и назначить фрагмент, где есть 1 s:

a_view = arr[:,2:,2:]
a_view[a_view.astype('bool')] = np.array(values).ravel()
print(arr)
array([[[ 0.,  1., 43., 25., 21.],
        [ 0.,  0.,  0.,  0.,  0.],
        [ 0., 43.,  0.,  2.,  0.],
        [ 0., 43.,  3.,  0.,  1.],
        [ 0., 45.,  0.,  4.,  0.]],

       [[ 0.,  1., 38., 29., 46.],
        [ 0.,  0.,  0.,  0.,  0.],
        [ 0., 32.,  0.,  0.,  4.],
        [ 0., 26.,  0.,  0.,  1.],
        [ 0., 30.,  5.,  9.,  0.]]])
...