【问题标题】:Is there a MATLAB accumarray equivalent in numpy?numpy 中是否有等效的 MATLAB accumarray?
【发布时间】:2020-05-19 20:55:03
【问题描述】:

我正在为 MATLAB 的 accumarray 在 numpy 中寻找一个快速的解决方案。 accumarray 累积数组中属于同一索引的元素。一个例子:

a = np.arange(1,11)
# array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])
accmap = np.array([0,1,0,0,0,1,1,2,2,1])

结果应该是

array([13, 25, 17])

到目前为止我做了什么: 我已经尝试了recipe here 中的accum 函数,它运行良好但速度很慢。

accmap = np.repeat(np.arange(1000), 20)
a = np.random.randn(accmap.size)
%timeit accum(accmap, a, np.sum)
# 1 loops, best of 3: 293 ms per loop

然后我尝试使用solution here,它应该工作得更快,但它不能正常工作:

accum_np(accmap, a)
# array([  1.,   2.,  12.,  13.,  17.,  10.])

有没有内置的 numpy 函数可以像这样进行累加?还是有其他建议?

【问题讨论】:

  • 我的博文已过期。试试 github 版本。它有一个覆盖良好的测试套件。
  • @Michael 和我创建了一个名为 numpy-groupies 的包,其中包含一个名为 aggregate 的类似 accumarray 的函数。有关详细信息,请参阅下面的答案。

标签: python numpy accumulator


【解决方案1】:

np.bincountweights 可选参数一起使用。在您的示例中,您会这样做:

np.bincount(accmap, weights=a)

【讨论】:

    【解决方案2】:

    聚会迟到了,但是……

    正如@Jamie 所说,对于求和的情况,np.bincount 既快速又简单。但是在更一般的情况下,对于其他ufuncs,例如maximum,您可以使用np.ufunc.at 方法。

    我已经将 a gist[参见下面的链接] 放在一起,将其封装在一个类似 Matlab 的接口中。它还利用重复的索引规则来提供'last''first' 函数,并且与Matlab 不同,'mean' 进行了明智的优化(在Matlab 中使用@mean 调用accumarray 非常慢,因为它运行一个非-每个组的内置函数,这很愚蠢)。

    请注意,我没有特别测试要点,但希望将来通过额外的功能和错误修复对其进行更新。

    2015 年 5 月/6 月更新:我已经重新设计了我的实现 - 它现在可以作为 ml31415/numpy-groupies 的一部分在 PyPi (pip install numpy-groupies) 上使用。基准如下(有关最新值,请参阅 github repo)...

    function  pure-py  np-grouploop   np-ufuncat np-optimised    pandas        ratio
         std  1737.8ms       171.8ms     no-impl       7.0ms    no-impl   247.1: 24.4:  -  : 1.0 :  -  
         all  1280.8ms        62.2ms      41.8ms       6.6ms    550.7ms   193.5: 9.4 : 6.3 : 1.0 : 83.2
         min  1358.7ms        59.6ms      42.6ms      42.7ms     24.5ms    55.4: 2.4 : 1.7 : 1.7 : 1.0 
         max  1538.3ms        55.9ms      38.8ms      37.5ms     18.8ms    81.9: 3.0 : 2.1 : 2.0 : 1.0 
         sum  1532.8ms        62.6ms      40.6ms       1.9ms     20.4ms   808.5: 33.0: 21.4: 1.0 : 10.7
         var  1756.8ms       146.2ms     no-impl       6.3ms    no-impl   279.1: 23.2:  -  : 1.0 :  -  
        prod  1448.8ms        55.2ms      39.9ms      38.7ms     20.2ms    71.7: 2.7 : 2.0 : 1.9 : 1.0 
         any  1399.5ms        69.1ms      41.1ms       5.7ms    558.8ms   246.2: 12.2: 7.2 : 1.0 : 98.3
        mean  1321.3ms        88.3ms     no-impl       4.0ms     20.9ms   327.6: 21.9:  -  : 1.0 : 5.2 
    Python 2.7.9, Numpy 1.9.2, Win7 Core i7.
    

    这里我们使用从[0, 1000) 统一挑选的100,000 索引。具体来说,大约 25% 的值是 0(用于布尔运算),其余的均匀分布在 [-50,25) 上。时间显示为 10 次重复。

    • purepy - 只使用纯 python,部分依赖 itertools.groupby
    • np-grouploop - 使用numpy 根据idx 对值进行排序,然后使用split 创建单独的数组,然后循环遍历这些数组,为每个数组运行相关的numpy 函数数组。
    • np-ufuncat - 使用 numpy ufunc.at 方法,它比它应该的要慢 - 正如我在 numpy 的 github repo 上创建的 an issue 中失败的那样。
    • np-optimisied - 使用自定义 numpy 索引/其他技巧来击败上述两种实现(min max prod 除外,它依赖于 ufunc.at)。
    • 熊猫 - pd.DataFrame({'idx':idx, 'vals':vals}).groupby('idx').sum()

    请注意,某些no-impls 可能没有根据,但我还没有费心让它们工作。

    正如 github 上所解释的,accumarray 现在支持以nan 为前缀的函数(例如nansum)以及sortrsortarray。它也适用于多维索引。

    【讨论】:

    • 干得好,伙计们。我正在尝试使用你的例程。可悲的是,我无法重现与 matlab 相同的结果,而且对于多维数组,理解它的工作原理也很复杂。你能帮帮我吗?
    • 最好在 github repo 上发布错误报告(提供最小的代码示例会有所帮助)
    • 感谢您的回答。我会提出一个问题,调用 numpy groupies aggregate
    【解决方案3】:

    我用scipy.weave 编写了一个 accumarray 实现并上传到 github:https://github.com/ml31415/numpy-groupies

    【讨论】:

      【解决方案4】:

      您可以在一行中使用 pandas DataFrame 完成此操作。

      In [159]: df = pd.DataFrame({"y":np.arange(1,11),"x":[0,1,0,0,0,1,1,2,2,1]})
      
      In [160]: df
      Out[160]: 
         x   y
      0  0   1
      1  1   2
      2  0   3
      3  0   4
      4  0   5
      5  1   6
      6  1   7
      7  2   8
      8  2   9
      9  1  10
      
      In [161]: pd.pivot_table(df,values='y',index='x',aggfunc=sum)
      Out[161]: 
          y
      x    
      0  13
      1  25
      2  17
      

      您可以告诉pivot_table 使用特定列作为索引和值,并获取一个新的 DataFrame 对象。当您将聚合函数指定为 sum 时,结果将与 Matlab 的 accumarray 相同。

      【讨论】:

        【解决方案5】:

        不如公认的答案好,但是:

        [np.sum([a[x] for x in y]) for y in [list(np.where(accmap==z)) for z in np.unique(accmap).tolist()]]
        

        这需要108us per loop(100000 次循环,最好的 3 次)

        接受的答案 (np.bincount(accmap, weights=a) 采用 2.05us per loop(100000 次循环,最好的 3 次)

        【讨论】:

          【解决方案6】:

          以下内容如何:

          import numpy
          
          def accumarray(a, accmap):
          
              ordered_indices = numpy.argsort(accmap)
          
              ordered_accmap = accmap[ordered_indices]
          
              _, sum_indices = numpy.unique(ordered_accmap, return_index=True)
          
              cumulative_sum = numpy.cumsum(a[ordered_indices])[sum_indices-1]
          
              result = numpy.empty(len(sum_indices), dtype=a.dtype)
              result[:-1] = cumulative_sum[1:]
              result[-1] = cumulative_sum[0]
          
              result[1:] = result[1:] - cumulative_sum[1:]
          
              return result
          

          【讨论】:

            【解决方案7】:

            这取决于你到底想做什么,但是 numpy unique 有一堆可选的输出,你可以用它们来积累。如果您的数组有多个相同的值,则 unique 将通过将 return_counts 选项设置为 true 来计算有多少相同的值。在一些简单的应用程序中,这就是您需要做的所有事情。

            numpy.unique(ar, return_index=False, return_inverse=False, return_counts=True, axis=None)
            

            您也可以将索引设置为 true 并使用它来累积不同的数组。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-10-20
              • 2016-08-27
              • 1970-01-01
              相关资源
              最近更新 更多