使用视图并获得免费运行时!将通用的n-dim
数组扩展到n+1-dim
在NumPy 1.10.0
中引入了numpy.broadcast_to
,我们可以利用它简单地生成一个2D
输入数组的3D
视图。好处是没有额外的内存开销,几乎免费的运行时间。这在数组很大且我们可以使用视图时是必不可少的。此外,这也适用于通用的n-dim
情况。
我会使用stack
代替copy
,因为读者可能会将其与创建内存副本的数组复制混淆。
沿第一个轴堆叠
如果我们想要沿着第一个轴堆叠输入的arr
,则使用np.broadcast_to
创建3D
视图的解决方案为 -
np.broadcast_to(arr,(3,)+arr.shape)
沿第三/最后一个轴堆叠
要沿第三个轴堆叠输入的arr
,创建3D
视图的解决方案是 -
np.broadcast_to(arr[...,None],arr.shape+(3,))
如果我们确实需要进行内存复制,我们可以在此处添加
.copy()
。因此,解决方案为 -
np.broadcast_to(arr,(3,)+arr.shape).copy()
np.broadcast_to(arr[...,None],arr.shape+(3,)).copy()
这是两种情况下的堆叠方式,以一个示例案例的形状信息展示 -
# Create a sample input array of shape (4,5)
In [55]: arr = np.random.rand(4,5)
# Stack along first axis
In [56]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[56]: (3, 4, 5)
# Stack along third axis
In [57]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[57]: (4, 5, 3)
相同的解决方案可用于将n-dim
输入扩展到沿第一个和最后一个轴的n+1-dim
视图输出。让我们探索一些更高维度的情况 -
3D输入情况:
In [58]: arr = np.random.rand(4,5,6)
# Stack along first axis
In [59]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[59]: (3, 4, 5, 6)
# Stack along last axis
In [60]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[60]: (4, 5, 6, 3)
4D输入案例:
In [61]: arr = np.random.rand(4,5,6,7)
# Stack along first axis
In [62]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[62]: (3, 4, 5, 6, 7)
# Stack along last axis
In [63]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[63]: (4, 5, 6, 7, 3)
等等。
时间
让我们使用一个大样本的2D
案例,获取时序并验证输出是否为view
。
# Sample input array
In [19]: arr = np.random.rand(1000,1000)
让我们证明所提出的解决方案确实是一个视图。我们将沿着第一个轴进行堆叠(如果沿着第三个轴进行堆叠,结果会非常相似)-
In [22]: np.shares_memory(arr, np.broadcast_to(arr,(3,)+arr.shape))
Out[22]: True
让我们来展示一下它几乎是免费的时间 -
In [20]: %timeit np.broadcast_to(arr,(3,)+arr.shape)
100000 loops, best of 3: 3.56 µs per loop
In [21]: %timeit np.broadcast_to(arr,(3000,)+arr.shape)
100000 loops, best of 3: 3.51 µs per loop
作为一个视图,在将N从3增加到3000时,对时间没有任何影响,并且两者在时间单位上都可以忽略不计。因此,既高效又节省内存和性能!
np.array([a]*3)
会产生一个形状为(3,r,c)的数组,而我认为OP想要的是(r,c,3)。 - mins(3,2,2)
。 - undefined