【问题标题】:Grouping continguous values in a numpy array with their length将 numpy 数组中的连续值及其长度分组
【发布时间】:2016-09-20 12:51:15
【问题描述】:

在 numpy / scipy(或纯 python,如果您愿意)中,将 numpy 数组中的连续区域分组并计算这些区域的长度的好方法是什么?

类似这样的:

x = np.array([1,1,1,2,2,3,0,0,0,0,0,1,2,3,1,1,0,0,0])
y = contiguousGroup(x)
print y

>> [[1,3], [2,2], [3,1], [0,5], [1,1], [2,1], [3,1], [1,2], [0,3]]

我尝试仅使用循环来执行此操作,但是它需要比我想要的更长的时间(6 秒)来创建一个包含大约 3000 万个样本和 20000 个连续区域的列表。

编辑:

现在进行一些速度比较(仅使用 time.clock() 和几百次迭代,如果以秒为单位,则更少)。

首先我的 python 循环代码在 5 个样本上进行了测试。

Number of elements  33718251
Number of regions  135137
Time taken = 8.644007 seconds...

Number of elements  42503100
Number of regions  6985
Time taken = 10.533305 seconds...

Number of elements  21841302
Number of regions  7619335
Time taken = 7.671015 seconds...

Number of elements  19723928
Number of regions  10799
Time taken = 5.014807 seconds...

Number of elements  16619539
Number of regions  19293
Time taken = 4.207359 seconds...

现在有了 Divakar 的矢量化解决方案。

Number of elements  33718251
Number of regions  135137
Time taken = 0.063470 seconds...

Number of elements  42503100
Number of regions  6985
Time taken = 0.046293 seconds...

Number of elements  21841302
Number of regions  7619335
Time taken = 1.654288 seconds...

Number of elements  19723928
Number of regions  10799
Time taken = 0.022651 seconds...

Number of elements  16619539
Number of regions  19293
Time taken = 0.021189 seconds...

修改后的方法给出的时间大致相同(在最坏的情况下可能慢 5%)

现在使用 Kasramvd 的生成器方法。

Number of elements  33718251
Number of regions  135137
Time taken = 3.834922 seconds...

Number of elements  42503100
Number of regions  6985
Time taken = 4.785480 seconds...

Number of elements  21841302
Number of regions  7619335
Time taken = 6.806867 seconds...

Number of elements  19723928
Number of regions  10799
Time taken = 2.264413 seconds...

Number of elements  16619539
Number of regions  19293
Time taken = 1.778873 seconds...

现在是他的 numpythonic 版本。

Number of elements  33718251
Number of regions  135137
Time taken = 0.286336 seconds...

Number of elements  42503100
Number of regions  6985
Time taken = 0.174769 seconds...

Memory error sample 3 (too many regions)

Number of elements  19723928
Number of regions  10799
Time taken = 0.087028 seconds...

Number of elements  16619539
Number of regions  19293
Time taken = 0.084963 seconds...

无论如何,我认为这个故事的寓意是 numpy 非常好。

【问题讨论】:

  • 开始你在第 1 行缺少一个结束括号。
  • 您能否让我们知道您使用建议的解决方案可能会获得什么样的加速(如果有)?
  • 当然,我会将您的解决方案与我的解决方案和其他解决方案进行比较。

标签: python arrays performance numpy


【解决方案1】:

这是一种矢量化方法 -

idx = np.concatenate(([0],np.flatnonzero(x[:-1]!=x[1:])+1,[x.size]))
out = zip(x[idx[:-1]],np.diff(idx))

示例运行 -

In [34]: x
Out[34]: array([1, 1, 1, 2, 2, 3, 0, 0, 0, 0, 0, 1, 2, 3, 1, 1, 0, 0, 0])

In [35]: out
Out[35]: [(1, 3), (2, 2), (3, 1), (0, 5), (1, 1), (2, 1), (3, 1), (1, 2), (0, 3)]

整个数组的串联可能会很昂贵。因此,可以建议一个在组移位索引上进行连接的修改版本,就像这样 -

idx0 = np.flatnonzero(x[:-1]!=x[1:])
count = np.concatenate(([idx0[0]+1],np.diff(idx0),[x.size-idx0[-1]-1]))
out = zip(x[np.append(0,idx0+1)],count)

或者,在最后一步,如果作为 2D 数组的输出没问题,我们可以避免 zipping 并使用 NumPy 的 column_stack,就像这样 -

out = np.column_stack((x[np.append(0,idx0+1)],count))

【讨论】:

    【解决方案2】:

    这是一个 Numpyhonic-pythonic 方法:

    In [192]: [(i[0], len(i)) for i in np.split(x, np.where(np.diff(x) != 0)[0]+1)]
    Out[192]: [(1, 3), (2, 2), (3, 1), (0, 5), (1, 1), (2, 1), (3, 1), (1, 2), (0, 3)]
    

    这是使用itertools.groupby() 的基于生成器的方法:

    In [180]: from itertools import groupby
    In [181]: [(k, sum(1 for _ in g)) for k, g in groupby(x)]
    Out[181]: [(1, 3), (2, 2), (3, 1), (0, 5), (1, 1), (2, 1), (3, 1), (1, 2), (0, 3)]
    

    或者:

    In [213]: mask = np.diff(x) != 0
    
    In [216]: np.column_stack((np.concatenate((x[mask], [x[-1]])), map(len, np.split(x, np.where(mask)[0]+1))))
    Out[216]: 
    array([[1, 3],
           [2, 2],
           [3, 1],
           [0, 5],
           [1, 1],
           [2, 1],
           [3, 1],
           [1, 2],
           [0, 3]])
    

    【讨论】:

      【解决方案3】:

      您可能只需要np.diff 并且更容易阅读。创建一个蒙版...

      x    = np.array([1,1,1,2,2,3,0,0,0,0,0,1,2,3,1,1,0,0,0])
      mask = np.where( np.diff(x) != 0)[0]
      mask = np.hstack((-1, mask, len(x)-1 ))
      
      zip( x[mask[1:]], np.diff(mask) )
      

      这应该是最容易理解并且完全矢量化的(不确定zip)...

      【讨论】:

        猜你喜欢
        • 2014-08-12
        • 2021-05-08
        • 1970-01-01
        • 1970-01-01
        • 2015-06-20
        • 2013-12-17
        • 1970-01-01
        • 2022-11-11
        • 1970-01-01
        相关资源
        最近更新 更多