我有一个形状为(3,)的numpy ndarray。我有另一个形状为(3,100,100)的ndarray。以下作品:
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]
虽然这很丑陋,但我希望它有助于理解问题和所需的行为。
答案 0 :(得分:2)
不只是看形状!
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
是一个非常不同的数组-它是带有数字dtype的3d。将0
替换为d[3]
会带来很大的不同。
In [88]: c_wrong.shape
Out[88]: (3, 10, 10)
In [89]: c_wrong.dtype
Out[89]: dtype('float64')
要从(3,N,N)中减去(3,),必须将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
中的in不能是因为标量为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
无效。