np.concatenate ND张量/数组与1D数组

时间:2018-01-02 19:25:21

标签: python arrays numpy concatenation

我有两个阵列a& B'/ P>

a.shape
(5, 4, 3)
array([[[ 0.        ,  0.        ,  0.        ],
        [ 0.        ,  0.        ,  0.        ],
        [ 0.        ,  0.        ,  0.        ],
        [ 0.10772717,  0.604584  ,  0.41664413]],

       [[ 0.        ,  0.        ,  0.        ],
        [ 0.        ,  0.        ,  0.        ],
        [ 0.10772717,  0.604584  ,  0.41664413],
        [ 0.95879616,  0.85575133,  0.46135877]],

       [[ 0.        ,  0.        ,  0.        ],
        [ 0.10772717,  0.604584  ,  0.41664413],
        [ 0.95879616,  0.85575133,  0.46135877],
        [ 0.70442301,  0.74126523,  0.88965603]],

       [[ 0.10772717,  0.604584  ,  0.41664413],
        [ 0.95879616,  0.85575133,  0.46135877],
        [ 0.70442301,  0.74126523,  0.88965603],
        [ 0.8039435 ,  0.62802183,  0.58885027]],

       [[ 0.95879616,  0.85575133,  0.46135877],
        [ 0.70442301,  0.74126523,  0.88965603],
        [ 0.8039435 ,  0.62802183,  0.58885027],
        [ 0.95848603,  0.72429311,  0.71461332]]])

和b

array([ 0.79212707,  0.66629398,  0.58676553], dtype=float32)
b.shape
(3,)

我想获得数组

ab.shape
(5,5,3)

我做如下 第一

b = b.reshape(1,1,3)

然后

b=np.concatenate((b, b,b, b, b), axis = 0)

ab=np.concatenate((a, b), axis = 1)
ab.shape
(5, 5, 3)

我得到了正确的结果,但特别是在步骤

时不太方便
b=np.concatenate((b, b,b, b, b), axis = 0)

当我必须多次键入时(真实数据集有很多维度)。有没有更快的方法来达到这个结果?

4 个答案:

答案 0 :(得分:4)

只需将b广播到3D,然后沿第二轴连接 -

b3D = np.broadcast_to(b,(a.shape[0],1,len(b)))
out = np.concatenate((a,b3D),axis=1)

broadcasting np.broadcast_to部分没有实际复制或复制,只是一个复制的视图,然后在下一步中,我们进行连接,即时复制

基准

我们正在将np.repeat version from @cᴏʟᴅsᴘᴇᴇᴅ's solutionnp.broadcast_to进行比较  在本节中,重点关注性能。基于广播的一个在第二步中执行复制和连接,作为合并命令可以这么说,而np.repeat版本进行复制,然后在两个单独的步骤中连接。

整体方法的时间安排:

案例#1:a = (500,400,300)b = (300,)

In [321]: a = np.random.rand(500,400,300)

In [322]: b = np.random.rand(300)

In [323]: %%timeit
     ...: b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)
     ...: r = np.concatenate((a, b3D), axis=1)
10 loops, best of 3: 72.1 ms per loop

In [325]: %%timeit
     ...: b3D = np.broadcast_to(b,(a.shape[0],1,len(b)))
     ...: out = np.concatenate((a,b3D),axis=1)
10 loops, best of 3: 72.5 ms per loop

对于较小的输入形状,由于设置广播所需的工作显然更复杂,因此拨打np.broadcast_to的时间会比np.repeat稍长,因为时间安排如下:

In [360]: a = np.random.rand(5,4,3)

In [361]: b = np.random.rand(3)

In [366]: %timeit np.broadcast_to(b,(a.shape[0],1,len(b)))
100000 loops, best of 3: 3.12 µs per loop

In [367]: %timeit b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)
1000000 loops, best of 3: 957 ns per loop

但是,广播部分将具有与输入的形状无关的恒定时间,即3 u-sec部分将保持在该标记周围。对应方的时间:b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)取决于输入形状。所以,让我们深入挖掘,看看两种方法的连接步骤如何公平/行为。

深入挖掘

尝试深入挖掘连接部分消耗的程度:

In [353]: a = np.random.rand(500,400,300)

In [354]: b = np.random.rand(300)

In [355]: b3D = np.broadcast_to(b,(a.shape[0],1,len(b)))

In [356]: %timeit np.concatenate((a,b3D),axis=1)
10 loops, best of 3: 72 ms per loop

In [357]: b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)

In [358]: %timeit np.concatenate((a,b3D),axis=1)
10 loops, best of 3: 72 ms per loop

结论:似乎没有太大不同。

现在,让我们尝试一种情况,b所需的复制数量越大,b的元素数量就越多。

In [344]: a = np.random.rand(10000, 10, 1000)

In [345]: b = np.random.rand(1000)

In [346]: b3D = np.broadcast_to(b,(a.shape[0],1,len(b)))

In [347]: %timeit np.concatenate((a,b3D),axis=1)
10 loops, best of 3: 130 ms per loop

In [348]: b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)

In [349]: %timeit np.concatenate((a,b3D),axis=1)
10 loops, best of 3: 141 ms per loop

结论:似乎合并的连接+与np.broadcast_to的复制在这里做得更好。

让我们试试(5,4,3)形状的原始案例:

In [360]: a = np.random.rand(5,4,3)

In [361]: b = np.random.rand(3)

In [362]: b3D = np.broadcast_to(b,(a.shape[0],1,len(b)))

In [363]: %timeit np.concatenate((a,b3D),axis=1)
1000000 loops, best of 3: 948 ns per loop

In [364]: b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)

In [365]: %timeit np.concatenate((a,b3D),axis=1)
1000000 loops, best of 3: 950 ns per loop

结论:再次,并没有太大的不同。

所以,最后的结论是,如果b中有很多元素,并且a的第一个轴也是一个大数字(因为复制数是那个),{ {1}}是一个不错的选择,否则基于np.broadcast_to的版本可以很好地处理其他情况。

答案 1 :(得分:3)

您可以使用np.repeat

r = np.concatenate((a, b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)), axis=1)

这样做,首先重塑您的b数组,使其与a的维度相匹配,然后根据a&#39},根据需要多次重复其值。第一轴:

b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)

array([[[1, 2, 3]],

       [[1, 2, 3]],

       [[1, 2, 3]],

       [[1, 2, 3]],

       [[1, 2, 3]]])

b3D.shape
(5, 1, 3)

然后将此中间结果与a -

连接起来
r = np.concatenate((a, b3d), axis=0)

r.shape
(5, 5, 3)

这与您当前的答案不同,主要在于重复值不是硬编码的事实(即重复处理)。

如果您需要针对不同数量的维度(而不是3D数组)处理此问题,则需要进行一些更改(主要是如何删除b的硬编码重塑)。

<强>计时

a = np.random.randn(100, 99, 100)
b = np.random.randn(100)

# Tai's answer
%timeit np.insert(a, 4, b, axis=1)
100 loops, best of 3: 3.7 ms per loop

# Divakar's answer
%%timeit 
b3D = np.broadcast_to(b,(a.shape[0],1,len(b)))
np.concatenate((a,b3D),axis=1)

100 loops, best of 3: 3.67 ms per loop

# solution in this post
%timeit np.concatenate((a, b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)), axis=1)
100 loops, best of 3: 3.62 ms per loop

这些都是非常有竞争力的解决方案。但请注意,性能取决于您的实际数据,因此请务必先测试一下!

答案 2 :(得分:1)

以下是一些基于cᴏʟᴅsᴘᴇᴇᴅ和Divakar解决方案的简单时间:

%timeit np.concatenate((a, b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)), axis=1)

输出: 最慢的运行时间比最快的运行时长6.44倍。这可能意味着正在缓存中间结果。 100000个循环,最佳3:3.68μs/循环

%timeit np.concatenate((a, np.broadcast_to(b[None,None], (a.shape[0], 1, len(b)))), axis=1)

输出: 最慢的运行速度比最快运行时长4.12倍。这可能意味着正在缓存中间结果。 100000个循环,最佳3:每循环10.7μs

现在,这是基于原始代码的时间:

%timeit original_func(a, b)

输出: 最慢的运行时间比最快的运行时间长4.62倍。这可能意味着正在缓存中间结果。 100000个循环,最佳3:每循环4.69μs

由于问题要求更快的方法来得出相同的结果,我会根据这些问题计算得到cᴏʟᴅsᴘᴇᴇᴅ的解决方案。

答案 3 :(得分:0)

您也可以使用np.insert

b_broad = np.expand_dims(b, axis=0) # b_broad.shape = (1, 3)
ab = np.insert(a, 4, b_broad, axis=1)
""" 
Because now we are inserting along axis 1
     a'shape without axis 1 = (5, 3) 
     b_broad's shape          (1, 3)  
can be aligned and broadcast b_broad to (5, 3)
"""

在此示例中,我们沿轴1插入,并将b_broad放在给定的索引之前,此处为4。换句话说,b_broad将在轴的长度上占据索引4,并使ab.shape等于(5, 5, 3)

再次

注意,在我们进行插入之前,我们将b转换为b_broad,以便安全地实现您想要的正确广播。 b的维度较小,插入时会有广播。我们可以使用expand_dims来实现这一目标。

如果a具有(3, 4, 5)形状,则如果沿轴1插入,则需要b_broad形状(3, 1)以匹配尺寸。这可以通过< / p>

b_broad = np.expand_dims(b, axis=1)  # shape = (3, 1)

b_broad设置为正确的形状是一种很好的做法,因为您可能拥有a.shape = (3, 4, 3),并且您确实需要指定在这种情况下广播的方式!

时间安排

来自OP的数据集:COLDSPEED的答案快了3倍。

def Divakar():  # Divakar's answer
    b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)
    r = np.concatenate((a, b3D), axis=1)
# COLDSPEED's result
%timeit np.concatenate((a, b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)), axis=1)
2.95 µs ± 164 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# Divakar's result
%timeit Divakar()
3.03 µs ± 173 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# Mine's
%timeit np.insert(a, 4, b, axis=1)
10.1 µs ± 220 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

数据集2(从COLDSPEED借用时间实验):在这种情况下无法得出结论,因为它们具有几乎相同的均值和标准差。

a = np.random.randn(100, 99, 100)
b = np.random.randn(100)

# COLDSPEED's result
%timeit np.concatenate((a, b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)), axis=1) 
2.37 ms ± 194 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# Divakar's
%timeit Divakar()
2.31 ms ± 249 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# Mine's
%timeit np.insert(a, 99, b, axis=1) 
2.34 ms ± 154 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

速度取决于数据的大小,形状和体积。如果您关心速度,请在您的数据集上进行测试。