简体   繁体   English

关于小块和小块广播的困惑

[英]Confusion regarding np.tile and numpy broadcasting

Suppose I have a 2D numpy array A of shape (m, n) . 假设我有一个形状为(m, n)的2D numpy数组A I would like to create a 3D array B of shape (m, n, k) such that B[:, :, l] is a copy of A for any slice l . 我想创建一个形状(m, n, k)的3D数组B ,使得B[:, :, l]是任何切片lA的副本。 I could think of two ways to do this: 我可以想到两种方法:

np.tile(A, (m, n, k))

or 要么

np.repeat(A[:, :, np.newaxis], k, axis=-1)

The first way seems simpler, but I the docs for np.tile mentioned: 第一种方法似乎更简单,但是我提到了np.tile的文档:

Note: Although tile may be used for broadcasting, it is strongly
recommended to use numpy's broadcasting operations and functions.

Why is this so, and is this an issue with np.repeat as well? 为什么会这样,这也是np.repeat的问题吗?

My other worry is that if m == n == k , then would np.tile() create confusion regarding which axis is augmented? 我的另一个担心是,如果m == n == k ,那么np.tile()引起关于哪个轴增加的混乱吗?

In summary, I have two questions: 总而言之,我有两个问题:

  1. Why is np.tile not preferred, and would m == n == k cause unexpected behavior in some cases? 为什么不优选np.tile ,并且在某些情况下m == n == k会导致意外行为?
  2. Which of the two ways above is more efficient in terms of time and memory? 在时间和内存方面,上述两种方法中哪一种更有效? Is there a cleaner or more efficient way than both approaches? 有没有比这两种方法更清洁或更有效的方法?

You say you want to expand a shape- (m, n) array and a shape- (n, k) array both to shape (m, n, k) and add them together. 您说要扩展一个shape- (m, n)数组和一个shape- (n, k)数组以同时成形(m, n, k)并将它们加在一起。 In that case, you don't need to physically expand your arrays at all; 在这种情况下,您根本不需要物理扩展阵列; aligning the axes and broadcasting will work fine: 对齐轴并进行广播将正常工作:

A = something of shape (m, n)
B = something of shape (n, k)

C = A[..., np.newaxis] + B

This requires no copying of the data in A and B , and should run much faster than anything involving a physical copy. 这不需要复制AB中的数据,并且运行速度要比涉及物理副本的任何东西都要快得多。

In [100]: A = np.arange(12).reshape(3,4)

Using repeat to add a new dimension at the end: 使用重复在末尾添加新尺寸:

In [101]: B = np.repeat(A[:,:,np.newaxis], 2, axis=-1)
In [102]: B.shape
Out[102]: (3, 4, 2)

Using tile and repeat to add a new dimension at the beginning: 使用平铺并重复以在开始时添加新尺寸:

In [104]: np.tile(A, (2,1,1)).shape
Out[104]: (2, 3, 4)
In [105]: np.repeat(A[None,:,:], 2, axis=0).shape
Out[105]: (2, 3, 4)

If we specify 2 repeats on the last dimension with tile, it gives a different shape 如果我们在图块的最后一个维度上指定2个重复,则其形状会有所不同

In [106]: np.tile(A, (1,1,2)).shape
Out[106]: (1, 3, 8)

Note what tile says about prepending a dimension with the repeats tuple is larger than the shape. 请注意,关于重复元组在尺寸前添加的tile表示的内容大于形状。

But if you used the expanded array in a calculation as described in the comments, you don't need to make a full repeated copy. 但是,如果您按照注释中的描述在计算中使用了扩展数组,则无需进行完整的重复复制。 Temporary views of the right shape can be used instead, taking advantage of broadcasting . 可以利用broadcasting来使用正确形状的临时视图。

In [107]: A1=np.arange(12).reshape(3,4)
In [108]: A2=np.arange(8).reshape(4,2)
In [109]: A3=A1[:,:,None] + A2[None,:,:]
In [110]: A3.shape
Out[110]: (3, 4, 2)
In [111]: A3
Out[111]: 
array([[[ 0,  1],
        [ 3,  4],
        [ 6,  7],
        [ 9, 10]],

       [[ 4,  5],
        [ 7,  8],
        [10, 11],
        [13, 14]],

       [[ 8,  9],
        [11, 12],
        [14, 15],
        [17, 18]]])

With the None ( np.newaxis ), the array views are (3,4,1) and (1,4,2) shaped, which broadcast together as (3,4,2). 使用Nonenp.newaxis ),数组视图的形状为(3,4,1)和(1,4,2),它们一起广播为(3,4,2)。 I could leave off the None in the 2nd case, since broadcasting will add the automatically. 在第二种情况下,我可以省略“ None ”,因为广播会自动添加。 But the trailing None is required. 但是尾随的None是必需的。

In [112]: (A1[:,:,None] + A2).shape
Out[112]: (3, 4, 2)

To add a 1d array (last dimension): 要添加一维数组(最后一维):

In [113]: (A1[:,:,None] + np.array([1,2])[None,None,:]).shape
Out[113]: (3, 4, 2)
In [114]: (A1[:,:,None] + np.array([1,2])).shape
Out[114]: (3, 4, 2)

Two basic broadcasting steps: 两个基本广播步骤:

  • add size 1 dimensions as the start as needed (automatic [None,....] ) 根据需要添加大小为1的尺寸作为开始(自动[None,....]
  • expand all size 1 dimensions to the shared size 将所有尺寸1的尺寸展开为共享尺寸

This set of calculations illustrate this: 这组计算说明了这一点:

In [117]: np.ones(2) + np.ones(3)
ValueError: operands could not be broadcast together with shapes (2,) (3,) 

In [118]: np.ones(2) + np.ones((1,3))
ValueError: operands could not be broadcast together with shapes (2,) (1,3) 

In [119]: np.ones(2) + np.ones((3,1))
Out[119]: 
array([[2., 2.],
       [2., 2.],
       [2., 2.]])
In [120]: np.ones((1,2)) + np.ones((3,1))
Out[120]: 
array([[2., 2.],
       [2., 2.],
       [2., 2.]])

with a missing middle dimension 缺少中间尺寸

In [126]: np.repeat(A[:,None,:],2,axis=1)+np.ones(4)
Out[126]: 
array([[[ 1.,  2.,  3.,  4.],
        [ 1.,  2.,  3.,  4.]],

       [[ 5.,  6.,  7.,  8.],
        [ 5.,  6.,  7.,  8.]],

       [[ 9., 10., 11., 12.],
        [ 9., 10., 11., 12.]]])

There is a more 'advanced' alternative (but not necessarily faster): 还有一个更“高级”的选择(但不一定更快):

In [127]: np.broadcast_to(A[:,None,:],(3,2,4))+np.ones(4)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM