【问题标题】:numpy array partial sums with weights带有权重的numpy数组部分和
【发布时间】:2013-12-08 09:05:56
【问题描述】:

我有一个 numpy 数组,例如 [a,b,c,d,e,...],并且想要计算一个看起来像 [x*a+y*b, x*b+y*c, x*c+y*d,...] 的数组。我的想法是首先将原始数组拆分为[[a,b],[b,c],[c,d],[d,e],...] 之类的东西,然后使用np.average 指定轴和权重(在我的情况下为x+y=1)攻击这个生物,甚至使用np.dot。不幸的是,我不知道如何创建这样的[a,b],[b,c],... 对数组。任何帮助,或完全不同的想法,甚至完成主要任务,都非常感谢:-)

【问题讨论】:

  • 我认为这个嵌套生成器有效:[(v1*x + v2*y) for v1, v2 in [arr[i:i+2] for i in xrange(len(arr)-1)]]

标签: python arrays numpy split slice


【解决方案1】:

如果你有一个小数组,我会创建一个移动的副本:

shifted_array=numpy.append(original_array[1:],0)
result_array=x*original_array+y*shifted_array

这里你必须在内存中存储你的数组两次,所以这个解决方案的内存效率很低,但你可以摆脱 for 循环。

如果你有大数组,你真的需要一个循环(但更需要一个列表理解):

result_array=[x*original_array[i]+y*original_array[i+1] for i in xrange(len(original_array)-1)]

它为您提供与 python 列表相同的结果,除了最后一项,无论如何都应该区别对待。

基于一些随机试验,用于小于 2000 项的数组。第一个解决方案似乎比第二个解决方案更快,但即使对于相对较小的阵列(我的 PC 上的几千个)也会遇到 MemoryError。

因此,一般来说,使用列表推导式,但如果您确定只在小型(最多 1-2000 个)数组上运行它,那么您有更好的机会。

创建一个像[[a,b],[b,c],[c,d],[d,e],...] 这样的新列表在内存和时间上都会效率低下,因为您还需要一个 for 循环(或类似的循环)来创建它,并且您必须将每个旧值存储在一个新数组中两次,所以您最终会将原始数组存储三遍。

【讨论】:

    【解决方案2】:

    最快、最简单的方法是手动提取数组的两个切片并将它们相加:

    >>> arr = np.arange(5)
    >>> x, y = 10, 1
    >>> x*arr[:-1] + y*arr[1:]
    array([ 1, 12, 23, 34])
    

    如果您想将其概括为三元组、四元组,这将变得很麻烦...但是您可以使用as_strided 以更通用的形式从原始数组创建对数组:

    >>> from numpy.lib.stride_tricks import as_strided
    
    >>> arr_pairs = as_strided(arr, shape=(len(arr)-2+1,2), strides=arr.strides*2)
    >>> arr_pairs
    array([[0, 1],
           [1, 2],
           [2, 3],
           [3, 4]])
    

    当然,使用as_strided 的好处在于,就像使用数组切片一样,不涉及数据复制,只是会扰乱查看内存的方式,因此创建这个数组几乎没有成本。

    现在最快的可能是使用np.dot

    >>> xy = [x, y]
    >>> np.dot(arr_pairs, xy)
    array([ 1, 12, 23, 34])
    

    【讨论】:

      【解决方案3】:

      这看起来像是 correlate 问题。

      a
      Out[61]: array([0, 1, 2, 3, 4, 5, 6, 7])
      
      b
      Out[62]: array([1, 2])
      
      np.correlate(a,b,mode='valid')
      Out[63]: array([ 2,  5,  8, 11, 14, 17, 20])
      

      根据数组大小和 BLAS dot 可以更快,你的里程会有很大差异:

      arr = np.random.rand(1E6)
      
      b = np.random.rand(2)
      
      np.allclose(jamie_dot(arr,b),np.convolve(arr,b[::-1],mode='valid'))
      True
      
      %timeit jamie_dot(arr,b)
      100 loops, best of 3: 16.1 ms per loop
      
      %timeit np.correlate(arr,b,mode='valid')
      10 loops, best of 3: 28.8 ms per loop
      

      这是一个英特尔 mkl BLAS 和 8 个内核,np.correlate 对于大多数实现来说可能会更快。

      @Jamie 的帖子中还有一个有趣的观察:

      %timeit b[0]*arr[:-1] + b[1]*arr[1:]
      100 loops, best of 3: 8.43 ms per loop
      

      他的评论还取消了np.convolve(a,b[::-1],mode=valid) 的使用,改为更简单的correlate 语法。

      【讨论】:

      • 这实际上更像是一个correlate 问题,但是是的,+1。实际上,如果您查看the source np.convolve(a, b) 调用np.correlate(a, b[::-1])
      • @Jamie 哦,太好了,我从来没有注意到这个功能——很高兴知道你可以在没有卷积欺骗的情况下做这种事情。
      • @Ophion,整洁,非常有见地!
      【解决方案4】:

      另一种方法是在数组a = np.array([a,b,c,d,e,...])中创建正确的pairs,根据数组b = np.array([x, y, ...])的大小reshape,然后利用numpy广播规则:

      a = np.arange(8) 
      b = np.array([1, 2])
      
      a = a.repeat(2)[1:-1]
      ans = a.reshape(-1, b.shape[0]).dot(b)
      

      时间(在我的电脑上):

      @Ophion's solution:
      # 100000 loops, best of 3: 4.67 µs per loop
      
      This solution:
      # 100000 loops, best of 3: 9.78 µs per loop
      

      所以,它更慢。 @Jaime 的解决方案更好,因为它不会像这样复制数据。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-06-04
        • 2020-10-24
        • 1970-01-01
        • 2017-07-23
        • 2019-02-18
        • 2012-04-26
        • 1970-01-01
        • 2011-12-15
        相关资源
        最近更新 更多