Ошибка трансляции неожиданной формы Numpy ndarray

#python #numpy #numpy-ndarray

#python #numpy #numpy-ndarray

Вопрос:

У меня есть один numpy 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,)
 

Есть ли способ преобразовать a (3,100,100) в a (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. Переменная r_1 не определена в вашем коде!

2. @JoeIddon извините, я пытался упростить проблему, чтобы ее можно было лучше понять, и забыл переименовать эту переменную. Я только что исправил это.

3. Нет проблем, мы ценим ваши усилия.

4. В первом примере вы получаете массив объектов; для меня это не имеет смысла. Что вы пытаетесь сделать?

5. @Daniel У меня есть функция, которая по заданному значению возвращает вектор. Теперь у меня есть сетка размером 100×100, когда я передаю эту сетку в функцию, она возвращает массив (3,100,100). Я просто хочу работать с этими векторами.

Ответ №1:

Посмотрите не только на форму!

 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 с числовым типом dtype. Замена этого 0 на d[3] имеет значение.

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

Чтобы вычесть a (3,) из a (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; in 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 должна совпадать со 2-й по последнюю из b . Вот почему я использовал reshape . И он передает задачу быстрой процедуре blas.

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

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

Комментарии:

1. О! Отлично. Я думаю, что [:,None,None] исправляет это. Все еще немного некрасиво. Разве у меня не может быть c с 3 элементами, все из которых являются массивами? Почему у меня может быть 3 элемента (2 массива 1 скаляр), но не 3 массива?

2. Это не только это вычитание. Все операторы отлично работают с версией [array, array, scalar]. Перекрестные продукты, точечные продукты, норма и т. Д. Но как только я добавляю 3-й массив, он жалуется на трансляцию

3. Я хотел бы иметь способ исправить это, изменив c_wrong (как-то сделать его массивом из 3 элементов, состоящим из 3 массивов). Таким образом, мне не пришлось бы реорганизовывать какой-либо код

4. Если бы я мог каким-то образом преобразовать это (N,) из a (N, M, M), все работало бы нормально. Следующий пример не так тривиален, как вычитание: a.точка (b) *** Ошибка значения: фигуры (2,2) и (2,200,157) не выровнены: 2 (dim 1) != 200 (dim 1) Не так тривиально, это работает, когда фигура (2,)вместо (2,200,157)