#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)