Numpy ndarray неожиданная ошибка трансляции формы - PullRequest
0 голосов
/ 14 апреля 2019

У меня есть один тупой ndarray с формой (3,).У меня есть другой ndarray с формой (3 100 100).Следующие работы:

a = np.array([1,1,1]) # Shape is (3,)
b = np.zeros((3,100,100)) # Shape is (3,100,100)
c = np.array([b[0], b[1], 0]) # Shape (3,)
c - a # works fine and as expected 

Но следующие разрывы:

c_wrong = np.array([b[0], b[1], b[2]]) # now c_wrong is (3,100,100) too

c_wrong - a # ValueError: operands could not be broadcast together with shapes (3,100,100) (3,)

Есть ли способ преобразовать (3 100 100) в (3,)?

Уродливая прогулка, которую я выяснил, это просто добавление фиктивного дополнительного компонента:

>>> c_wrong = np.array([b[0],b[1],b[2],0])
>>> a = np.array([1,1,1,1])
>>> d = c_wrong - a
>>> d[0:3]

Это довольно уродливо, но я надеюсь, что это поможет понять проблему и желаемоеповедение.

1 Ответ

2 голосов
/ 14 апреля 2019

Посмотрите на больше, чем на фигуру!

In [82]: a = np.array([1,1,1]) # Shape is (3,) 
    ...: b = np.zeros((3,10,10)) # Shape is (3,10,10) 
    ...: c = np.array([b[0], b[1], 0]) # Shape (3,)                             
In [83]:                                                                        
In [83]: c                                                                      
Out[83]: 
array([array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]),
       array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]),
       0], dtype=object)
In [84]: c.shape                                                                
Out[84]: (3,)

Да, c просто имеет 3 элемента, но каждый является массивом или скаляром (последние 0).

In [85]: c-a                                                                    
Out[85]: 
array([array([[-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]]),
       array([[-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]]),
       -1], dtype=object)

Таким образом, вам удалось вычесть 1 из каждого из этих элементов!

c_wrong - это совсем другой массив - это 3d с числовым типом d.Замена 0 на d[3] делает все различия.

In [88]: c_wrong.shape                                                          
Out[88]: (3, 10, 10)
In [89]: c_wrong.dtype                                                          
Out[89]: dtype('float64')

Чтобы вычесть (3,) из (3, N, N), вы должны отрегулировать размеры a до(3,1,1).Тогда он может сделать правильное вещание.

In [91]: c_wrong -  a[:,None,None]                                              
Out[91]: 
array([[[-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
        [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
        ....
        [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]]])

Я думаю, это просто случайность, что ваш c-a работает.Определив c с элементом 0, вы создали массив object dtype.Математика с объектными массивами dtype - это что-то среднее.Это вычитание является одним из тех хитов.Но не рассчитывай на это;есть много способов, которыми математика с таким массивом не работает - и она всегда медленнее.


c_wrong по сути то же самое, что b.


Ядро numpy - это многомерные числовые массивы.np.array по умолчанию пытается построить настолько высокий размерный размер, насколько это возможно.В вашем c_wrong случае он может сделать 3d;в c это невозможно из-за скаляра 0. Поэтому он прибегает к созданию массива объектов 1d.

Самый верный способ создать массив объектов нужной формы - инициализировать «пробел».один, и заполните его.Но даже тогда заполнение может быть сложно.Здесь мне удалось сделать это с помощью:

In [92]: c3 = np.empty(3, object)                                               
In [93]: c3                                                                     
Out[93]: array([None, None, None], dtype=object)
In [94]: c3[:] = list(b)                                                        
In [95]: c3                                                                     
Out[95]: 
array([array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       ....
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])], dtype=object)
In [96]: c3-a                                                                   
Out[96]: 
array([array([[-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.],
....
       [-1., -1., -1., -1., -1., -1., -1., -1., -1., -1.]])], dtype=object)

Заполнение, которое не работает:

In [97]: c3[:] = b                                                              
------------------------------------------------------------------------ 
...
ValueError: could not broadcast input array from shape (3,10,10) into shape (3)

a[:,None,None] не выглядит так ужасно, когда вы знакомы с вещанием.

Сравните время:

In [98]: timeit c_wrong-a[:,None,None]                                          
5.22 µs ± 6.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [99]: timeit c3-a                                                            
9.53 µs ± 20.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [100]: timeit c-a                                                            
7.66 µs ± 10.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Или с dot

In [103]: timeit np.dot(a, b.reshape(3,-1)).shape                              
2.44 µs ± 9.63 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [104]: timeit np.dot(a,c).shape                                              
10.9 µs ± 16.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [105]: timeit np.dot(a,c3).shape                                             
11.6 µs ± 30.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

dot имеет очень специфические правила - последняя ось a должна совпадатьот второго до последнего из b.Вот почему я использовал reshape.И он передает задачу в быструю подпрограмму 'blas'.

С массивом объектов (3,) он выполняет произведение 1d dot - но итеративно.

@, matmul работает с измененным b, но не с c или c3.То же самое для einsum: np.einsum('i,ijk->jk',a,b).shape работает, но ничего, используя c.

...