Как правильно разыменовывать при копировании части массива numpy? - PullRequest
0 голосов
/ 15 января 2020

В моем скрипте анализа я заметил странное поведение (хотя оно и было задумано) при копировании массивов в Python. Если у меня есть 2D-массив A, создайте другой массив B с записями из A, а затем нормализуйте B с длиной первого измерения A, записи в A изменятся в странный путь. Я могу воспроизвести проблему с помощью следующего кода:

foo = np.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 9.]])
startIndex = 1
print(foo)
for it, i in enumerate(foo):
    if not it:
        sum = i[startIndex:]
    else:
        sum += i[startIndex:]
print(foo)
sum /= foo.shape[0]
print(foo)

Вывод:

[[1. 2. 3.]
 [4. 5. 6.]
 [7. 8. 9.]]
[[ 1. 15. 18.]
 [ 4.  5.  6.]
 [ 7.  8.  9.]]
[[1. 5. 6.]
 [4. 5. 6.]
 [7. 8. 9.]]

Форма массива не имеет значения, но эта форма 3x3 показывает ее довольно хорошо. Я предполагаю, что sum = i[startIndex:] каким-то образом устанавливает ссылку на последние две записи foo[0] и изменения на sum также влияют на эти записи - но согласно этот вопрос Я догадался, что получу копию вместо ссылка. Как правильно получить копию только части массива?

Ответы [ 2 ]

0 голосов
/ 15 января 2020

Сначала заметка о конструкции массива. Массив имеет базовую c информацию, такую ​​как shape, dtype и strides, и 1d data buffer, где хранятся фактические значения.

Копия будет новым объектом массива, со своими собственными значениями.

Представление - это новый объект массива со своими собственными shape, et c, но он разделяет data buffer с исходным массивом. В качестве меры эффективности, numpy пытается сделать вид, где это возможно. Многие операции делают это, например reshape и transpose. Индексация также может сделать представление. basic indexing со скаляром или срезом создает представление, advanced indexing со списками, масками или массивами создает копию.

X.copy() является одним из способов принудительного копирования. np.array(X) также делает копию - если используется параметр по умолчанию copy.

In [113]: foo = np.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 9.]])

Итерация в 2d массиве, как и вы, выбирает последовательные «строки», каждая из которых представляет:

In [114]: for row in foo: 
     ...:     row[1] += 10     # modify the 2nd element of the row
     ...:                                                                                        
In [115]: foo                                                                                    
Out[115]: 
array([[ 1., 12.,  3.],
       [ 4., 15.,  6.],
       [ 7., 18.,  9.]])

Выбор строки по скалярному индексу делает то же самое:

In [116]: foo[0]                                                                                 
Out[116]: array([ 1., 12.,  3.])
In [117]: foo[0][1:]-=10                                                                         
In [118]: foo                                                                                    
Out[118]: 
array([[ 1.,  2., -7.],
       [ 4., 15.,  6.],
       [ 7., 18.,  9.]])
In [119]: foo[0,1:]                                                                              
Out[119]: array([ 2., -7.])

Становится знакомым с тем, когда вы получаете представление и когда копия является важной частью обучения numpy.

Таким образом, используя copy, чтобы сделать копию 1-й строки:

In [124]: foo = np.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 9.]])                                                                                             
In [126]: sum = foo[0,1:].copy() 
     ...: for row in foo[1:]:     # iterate on a slice (skips 1st row)
     ...:     sum += row[1:]      # select a slice of row (skips 1st element)                                            
In [127]: foo                # no change                                                                    
Out[127]: 
array([[1., 2., 3.],
       [4., 5., 6.],
       [7., 8., 9.]])
In [128]: sum                                                                                    
Out[128]: array([15., 18.])

Но мы могли бы также взять эту сумму без итерации:

In [129]: foo[:, 1:].sum(axis=0)   # select slice of columns, sum across rows                                                                 
Out[129]: array([15., 18.])

Лучший способ написать [126] итерацию (и, вероятно, близкую к тому, что sum реализует в скомпилированном коде):

In [200]: res = np.zeros(2, float)                                                               
In [201]: for row in foo: 
     ...:     res += row[1:] 
     ...:                                                                                        
In [202]: res                                                                                    
Out[202]: array([15., 18.])
0 голосов
/ 15 января 2020

Вы можете сделать копию по значению, используя конструктор np.array:

for it, i in enumerate(foo):
    if not it:
        s = np.array(i[startIndex:])
    else:
        s += i[startIndex:]

Обратите внимание, что я изменил имя переменной, чтобы избежать затенения встроенной функции sum.

...