【问题标题】:Combine or join numpy arrays合并或加入 numpy 数组
【发布时间】:2016-07-21 23:08:13
【问题描述】:

如何加入两个 numpy ndarray 以快速完成以下操作,使用优化的 numpy,而无需任何循环?

>>> a = np.random.rand(2,2)
>>> a
array([[ 0.09028802,  0.2274419 ],
       [ 0.35402772,  0.87834376]])

>>> b = np.random.rand(2,2)
>>> b
array([[ 0.4776325 ,  0.73690098],
       [ 0.69181444,  0.672248  ]])

>>> c = ???
>>> c
array([[ 0.09028802,  0.2274419, 0.4776325 ,  0.73690098],
       [ 0.09028802,  0.2274419, 0.69181444,  0.672248  ],
       [ 0.35402772,  0.87834376, 0.4776325 ,  0.73690098],
       [ 0.35402772,  0.87834376, 0.69181444,  0.672248  ]])

【问题讨论】:

  • np.hstack((a[[0, 0, 1, 1]], b[[0, 1, 0, 1]]) 会这样做。不过,大概有人可以想出一种更通用的方法。

标签: python arrays performance numpy vectorization


【解决方案1】:

不是最漂亮的,但你可以结合hstackrepeattile

>>> a = np.arange(4).reshape(2,2)
>>> b = a+10
>>> a
array([[0, 1],
       [2, 3]])
>>> b
array([[10, 11],
       [12, 13]])
>>> np.hstack([np.repeat(a,len(a),0),np.tile(b,(len(b),1))])
array([[ 0,  1, 10, 11],
       [ 0,  1, 12, 13],
       [ 2,  3, 10, 11],
       [ 2,  3, 12, 13]])

或者对于 3x3 的情况:

>>> a = np.arange(9).reshape(3,3)
>>> b = a+10
>>> np.hstack([np.repeat(a,len(a),0),np.tile(b,(len(b),1))])
array([[ 0,  1,  2, 10, 11, 12],
       [ 0,  1,  2, 13, 14, 15],
       [ 0,  1,  2, 16, 17, 18],
       [ 3,  4,  5, 10, 11, 12],
       [ 3,  4,  5, 13, 14, 15],
       [ 3,  4,  5, 16, 17, 18],
       [ 6,  7,  8, 10, 11, 12],
       [ 6,  7,  8, 13, 14, 15],
       [ 6,  7,  8, 16, 17, 18]])

【讨论】:

  • 这很棒。非常感谢。我知道我没有在我的问题中指定,但如果 a 和 b 是不同的维度,您的解决方案会产生错误。以下编辑更正了这种情况:np.hstack([np.repeat(a,len(b),0),np.tile(b,(len(a),1))])
  • 其实我上面的建议是行不通的。但试试b = np.random.rand(3,2) 我看到ValueError: all the input array dimensions except for the concatenation axis must match exactly
  • @HelloWorld:上述假设是在 ab 都是正方形且形状相同的假设下完成的,就像在您的示例中一样。一般情况需要处理吗?
  • 我确实需要处理一般情况,但您的回答对于我提出的问题是正确的。我确实做到了这一点:np.repeat(a[np.newaxis,:], b.shape[0], axis=1)[0,:]
  • @HelloWorld 不要自吹自擂,但我在下面的回答对两种不同形状的矩阵都适用。
【解决方案2】:

显然,您想要的是水平堆叠的ab 的笛卡尔积。您可以使用itertools 模块为numpy 数组生成索引,然后使用numpy.hstack 将它们堆叠起来:

import numpy as np
from itertools import product

a = np.array([[ 0.09028802,  0.2274419 ],
              [ 0.35402772,  0.87834376]])

b = np.array([[ 0.4776325 ,  0.73690098],
              [ 0.69181444,  0.672248  ],
              [ 0.79941110,  0.52273   ]])

a_inds, b_inds = map(list, zip(*product(range(len(a)), range(len(b)))))

c = np.hstack((a[a_inds], b[b_inds]))

这会产生c 的:

array([[ 0.09028802,  0.2274419 ,  0.4776325 ,  0.73690098],
       [ 0.09028802,  0.2274419 ,  0.69181444,  0.672248  ],
       [ 0.09028802,  0.2274419 ,  0.7994111 ,  0.52273   ],
       [ 0.35402772,  0.87834376,  0.4776325 ,  0.73690098],
       [ 0.35402772,  0.87834376,  0.69181444,  0.672248  ],
       [ 0.35402772,  0.87834376,  0.7994111 ,  0.52273   ]])

分解索引的东西:

product(range(len(a)), range(len(b)) 如果将其转换为列表,将生成如下所示的内容:

[(0, 0), (0, 1), (1, 0), (1, 1)]

你想要这样的东西:[0, 0, 1, 1][0, 1, 0, 1],所以你需要转置生成器。这样做的惯用方法是使用zip(*zipped_thing)。但是,如果你直接分配这些,你会得到tuples,像这样:

[(0, 0, 1, 1), (0, 1, 0, 1)]

但是 numpy 数组将元组解释为多维索引,因此您希望将它们转换为列表,这就是我将 list 构造函数映射到 product 函数的结果的原因。

【讨论】:

    【解决方案3】:

    让我们通过一个前瞻性的解决方案来处理涉及不同形状数组的通用案例,并使用一些内联的 cmets 来解释所涉及的方法。

    (1) 首先,我们存储输入数组的形状。

    ma,na = a.shape
    mb,nb = b.shape
    

    (2) 接下来,初始化一个 3D 数组,其列数是输入数组ab 中的列数之和。使用np.empty 完成此任务。

    out = np.empty((ma,mb,na+nb),dtype=a.dtype)
    

    (3) 然后,为第一个“na”列设置 3D 数组的第一个轴,其中行来自 aa[:,None,:]。因此,如果我们将其分配给out[:,:,:na],那么第二个冒号将向 NumPy 表明我们需要一个广播设置,如果可能的话,就像 NumPy 数组中的单例暗淡一样。实际上,这与平铺/重复相同,但可能是以一种有效的方式。

    out[:,:,:na] = a[:,None,:]
    

    (4) 重复将b 中的元素设置为输出数组。这次我们将沿out 的第一个轴和out[:,:,na:] 进行广播,第一个冒号帮助我们进行广播。

    out[:,:,na:] = b
    

    (5) 最后一步是将输出重塑为二维形状。这可以通过使用所需的 2D 形状元组简单地更改形状来完成。重塑只是改变观点,实际上是零成本。

    out.shape = (ma*mb,na+nb)
    

    浓缩一切,完整的实现看起来像这样 -

    ma,na = a.shape
    mb,nb = b.shape
    out = np.empty((ma,mb,na+nb),dtype=a.dtype)
    out[:,:,:na] = a[:,None,:]
    out[:,:,na:] = b
    out.shape = (ma*mb,na+nb)
    

    【讨论】:

    • 不是一个漂亮的班轮,但速度更快。
    • @hpaulj 有时丑的就是快的 :)
    • @Divakar 我有点惊讶这行得通。我没有意识到你可以分配一个shape 元组。你能准确解释发生了什么吗?
    • @Paul 请查看编辑。希望这些有意义:)
    • @Divakar 谢谢你的回答和额外的颜色。我选择这个作为答案有 3 个原因:1)处理通用案例,2)仅 numpy 和 3)性能。保罗的回答在 IMO 上也很好。
    【解决方案4】:

    您可以使用dstack()broadcast_arrays()

    import numpy as np
    
    a = np.random.randint(0, 10, (3, 2))
    b = np.random.randint(10, 20, (4, 2))
    
    np.dstack(np.broadcast_arrays(a[:, None], b)).reshape(-1, a.shape[-1] + b.shape[-1])
    

    【讨论】:

    • broadcast_arrays 的这种用法很聪明,但比 repeattile 的使用要慢。
    【解决方案5】:

    尝试 np.hstack 或 np.vstack。这甚至适用于长度不同的数组。您需要做的就是: np.hstack(appendedarray[:]) 或 np.vstack(appendedarray[:])

    【讨论】:

      【解决方案6】:

      所有数组都是可索引的,所以你可以通过调用来合并:

      a[:2],b[:2]
      

      或者您可以使用核心 numpy 堆栈函数,应该如下所示:

      c = np.vstack(a,b)
      

      【讨论】:

      • 第一件事就是创建一个 2-ple。第二个不起作用,但即使它起作用,它也只会将 b 附加到 a。 OP 想要一个 numpy 数组,它是两个数组的笛卡尔积。
      猜你喜欢
      • 2021-09-05
      • 1970-01-01
      • 2013-04-13
      • 1970-01-01
      • 2020-01-05
      • 2014-03-22
      • 2017-09-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多