是否覆盖Numpy中的广播约定?

a64a0gku  于 2022-12-18  发布在  其他
关注(0)|答案(1)|浏览(107)

我有两个Numpy数组xy,其中x是n维的,n〉=1,但在“编译时”未知,而y是一维的。y的(也是唯一的)维度。我想得到他们的“总和”,称之为s,与x的维度相同,如下所示:

import numpy as np

x  = np.random.randn(5,100,10)
y  = np.random.randn(5)

s = np.empty_like(x)
for i in range(x.shape[0]):
   s[i] = x[i] + y[i]

但我希望避免for循环,这既是出于可读性考虑,更重要的是出于速度考虑。
很明显,由于Numpy中的广播约定是如何工作的,我不能做x+y。这要么会抛出错误,要么更糟的是,会碰巧工作,给出意想不到的结果。
我找到了两个不错的俏皮话,

s1 = (x.T + y).T
s2 = x + y[(slice(0, y.shape[0]),) + (np.newaxis,)*(x.ndim-1)]

第一个是利用了x中的第一个维度与y中的唯一维度匹配的事实,否则就不起作用了,第二个更一般,但是非常冗长。
由于我还在学习Python和Numpy,我想知道是否有其他(理想情况下更好,但我也对一般情况感兴趣)替代方法来做我想做的事情。本质上,我可能正在寻找的是一种覆盖广播约定的方法...

6g8kf2rb

6g8kf2rb1#

你不能改变广播规则,所以无论如何你必须给y添加尾部维度。
您已经使用了newaxis,生成了:

In [9]: y[:,None,None].shape
Out[9]: (5, 1, 1)

reshape构造一个类似的元组可能要简单一些:

In [10]: y.reshape((-1,1,1)).shape
Out[10]: (5, 1, 1)

expand_dims是指定reshape的另一种方式:

In [11]: np.expand_dims(y,(1,2)).shape
Out[11]: (5, 1, 1)


这些都不是计算上昂贵的,即使代码最终有点冗长。
有几个atleast函数,但没有帮助:

In [19]: np.atleast_3d(y).shape
Out[19]: (1, 5, 1)

尽管如此,看看expand_dimsatleast的代码可能会给予你一些如何添加维度的想法,不管怎样,他们使用newaxisreshape
也可以使用np.array指定尺寸,但这会添加前导尺寸:

In [22]: np.array(y, ndmin=3, copy=False).shape
Out[22]: (1, 1, 5)

编辑

使用x.ndim

In [30]: dim=[1]*x.ndim; dim[0]=-1;y.reshape(dim).shape
Out[30]: (5, 1, 1)
In [44]: y.reshape((-1,)+(1,)*(x.ndim-1)).shape
Out[44]: (5, 1, 1)

In [33]: np.expand_dims(y,tuple(np.arange(1,x.ndim))).shape
Out[33]: (5, 1, 1)
In [36]: np.expand_dims(y,list(range(1,x.ndim))).shape
Out[36]: (5, 1, 1)

你的版本,稍微简化一下:

In [45]: y[((slice(None),)+(None,)*(x.ndim-1))].shape
Out[45]: (5, 1, 1)

在时间上,这是最快的。

相关问题