【问题标题】:Vectorize numpy array expansion向量化 numpy 数组扩展
【发布时间】:2015-07-30 12:35:35
【问题描述】:

我正在尝试找到一种方法来矢量化一个操作,其中我采用 1 个 numpy 数组并将每个元素扩展为 4 个新点。我目前正在使用 Python 循环进行操作。首先让我解释一下算法。

input_array = numpy.array([1, 2, 3, 4])

我想将此数组中的每个元素“扩展”或“扩展”到 4 个点。因此,元素零(值 1)将扩展为这 4 个点:

[0, 1, 1, 0]

这会发生在每个元素的最终数组中:

[0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 4, 4, 0]

我想让代码稍微通用,以便我也可以以不同的方式执行此“扩展”。例如:

input_array = numpy.array([1, 2, 3, 4])

这一次,每个点都通过向每个点添加 += .2 来扩展。所以,最终的数组是:

[.8, .8, 1.2, 1.2, 1.8, 1.8, 2.2, 2.2, 2.8, 2.8, 3.2, 3.2, 3.8, 3.8, 4.2, 4.2]

我目前使用的代码如下所示。这是一种非常幼稚的方法,但似乎有一种方法可以加快大型数组的速度:

output = []
for x in input_array:
    output.append(expandPoint(x))

output = numpy.concatenate(output)

def expandPoint(x):
    return numpy.array([0, x, x, 0])

def expandPointAlternativeStyle(x):
    return numpy.array([x - .2, x - .2, x + .2, x + .2])

【问题讨论】:

    标签: python arrays numpy vectorization


    【解决方案1】:

    我不确定您算法的逻辑,但我认为如果您希望每个点都指向extend,然后将它们全部排队,您最好的方法是增加维度,然后采取扁平化版本;对于您的第一个示例:

    >>> x = np.array([1,2,3,4])
    >>> x
    array([1, 2, 3, 4])
    >>> y = np.empty((len(x), 4))
    >>> y[:, [0, 3]] = 0
    >>> y[:, 1:3] = x[:, None]
    >>> y
    array([[ 0.,  1.,  1.,  0.],
           [ 0.,  2.,  2.,  0.],
           [ 0.,  3.,  3.,  0.],
           [ 0.,  4.,  4.,  0.]])
    >>> y.reshape((4*len(x),))  # Flatten it back
    array([ 0.,  1.,  1.,  0.,  0.,  2.,  2.,  0.,  0.,  3.,  3.,  0.,  0.,
        4.,  4.,  0.])
    

    然后你如何着手使那个通用取决于你的算法,我不完全确定要遵循......但这应该会给你一些开始的指导。

    编辑:正如其他人所说,您实际上可以用更简洁的方式使用外部产品来完成所有这些操作,这可能会更接近您的算法,例如,无耻地让 YXD 回答一个-班轮:

    >>> (x[:, None] * np.array([0,1,1,0])[None, :]).flatten()
    array([0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 4, 4, 0])
    

    但是,原则仍然是先进入更高维度(2),然后再将其扩展到原始维度(1)

    【讨论】:

      【解决方案2】:

      对于第一个例子,你可以使用np.kron

      >>> a = np.array([0, 1, 1, 0])
      >>> np.kron(input_array, a)
      array([0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 4, 4, 0])
      

      对于第二个示例,您可以使用np.repeatnp.tile

      >>> b = np.array([-0.2, -0.2, 0.2, 0.2])
      >>> np.repeat(input_array, b.size) + np.tile(b, input_array.size)
      array([ 0.8,  0.8,  1.2,  1.2,  1.8,  1.8,  2.2,  2.2,  2.8,  2.8,  3.2,
              3.2,  3.8,  3.8,  4.2,  4.2])
      

      【讨论】:

      • 这很高兴知道!但是,它在我的场景中不起作用,因为输入数字到处都是。它们不像 1、2、3、4 那样“常规”。我只是以它为例。
      • 你所说的“到处都是”是什么意思?
      • 我的意思是不可预测,我的词选择不好。我查看了iron,因为我以前从未见过它,而且它似乎会起作用。我之前认为你的解决方案与数字 0 和 1 相关,我错了。
      • 这个解决方案可能不是最好的,但我认为这是一种可能的想法。
      【解决方案3】:

      您似乎想要在 input_array 和包含扩展元素的数组之间进行元素操作。对于这些,您可以使用broadcasting

      对于第一个示例,您似乎正在执行elementwise multiplication -

      In [424]: input_array = np.array([1, 2, 3, 4])
           ...: extend_array = np.array([0, 1, 1, 0])
           ...: 
      
      In [425]: (input_array[:,None] * extend_array).ravel()
      Out[425]: array([0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 4, 4, 0])
      

      对于第二个示例,您似乎正在执行elementwise addition -

      In [422]: input_array = np.array([1, 2, 3, 4])
           ...: extend_array = np.array([-0.2, -0.2, 0.2, 0.2])
           ...: 
      
      In [423]: (input_array[:,None] + extend_array).ravel()
      Out[423]: 
      array([ 0.8,  0.8,  1.2,  1.2,  1.8,  1.8,  2.2,  2.2,  2.8,  2.8,  3.2,
              3.2,  3.8,  3.8,  4.2,  4.2])
      

      【讨论】:

      • 啊,我从没想过使用乘法和加法。现在我看到它似乎很明显。谢谢!
      • 这种方法和带有outerufunc 方法看起来是等价的,但我会选择这种方法,因为它是第一个。我对两者都进行了测试,它们在性能方面似乎相同。
      【解决方案4】:

      对于第一个示例,您可以对输入和模板进行外积并重塑结果:

      input_array = np.array([1, 2, 3, 4])
      template = np.array([0, 1, 1, 0])
      
      np.multiply.outer(input_array, template)
      # array([[0, 1, 1, 0],
      #        [0, 2, 2, 0],
      #        [0, 3, 3, 0],
      #        [0, 4, 4, 0]])
      
      result = np.multiply.outer(input_array, template).ravel()
      # array([0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 4, 4, 0])
      

      类似地,对于您的第二个示例,您可以使用 np.add.outer

      np.add.outer(input_array, [-0.2, -0.2, 0.2, 0.2]).ravel()
      # array([ 0.8,  0.8,  1.2,  1.2,  1.8,  1.8,  2.2,  2.2,  2.8,  2.8,  3.2,
              3.2,  3.8,  3.8,  4.2,  4.2])
      

      见:

      【讨论】:

      • 感谢您的链接。这看起来很有效,而且似乎是迄今为止最直接的。
      • 对于这个例子来说,使用对外部的调用与@divakar 下面的广播方法直接等效。我想知道是否有使用一个而不是另一个的约定?
      • 是的,它们等同于 np.multiplynp.add 是“ufuncs”,可以为您处理广播 - 请参阅 docs.scipy.org/doc/numpy/reference/ufuncs.html
      猜你喜欢
      • 2022-11-26
      • 1970-01-01
      • 2016-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-22
      • 1970-01-01
      • 2015-04-01
      相关资源
      最近更新 更多