【问题标题】:How to find the groups of consecutive elements in a NumPy array如何在 NumPy 数组中查找连续元素的组
【发布时间】:2011-11-13 05:44:56
【问题描述】:

我必须对 NumPy 数组中的连续元素进行聚类。考虑以下示例

a = [ 0, 47, 48, 49, 50, 97, 98, 99]

输出应该是如下的元组列表

[(0), (47, 48, 49, 50), (97, 98, 99)]

这里的区别只是元素之间的一个。如果差异也可以指定为限制或硬编码数字,那就太好了。

【问题讨论】:

标签: python numpy


【解决方案1】:

这听起来有点像家庭作业,所以如果你不介意我会建议一种方法

您可以使用

遍历列表
for i in range(len(a)):
    print a[i]

您可以测试列表中的下一个元素是否符合以下条件

if a[i] == a[i] + 1:
    print "it must be a consecutive run"

您可以将结果单独存储在

results = []

注意 - 上面隐藏了一个索引超出范围的错误,您需要处理

【讨论】:

  • 当存在更明显的解决方案时,请不要建议在 numpy 数组上使用 python 迭代器。它违背了使用 numpy 的目的。 (通常。)如果 OP 不关心性能,他们可能会使用 python 列表。
【解决方案2】:

(a[1:]-a[:-1])==1 将生成一个布尔数组,其中False 表示运行中的中断。也可以使用内置的numpy.grad

【讨论】:

  • 我不明白这个答案,尽管它是唯一一个看起来“功能性”(如功能性语言)的答案。您在这里所做的是将减号运算符应用于列表。我不明白这怎么行。
  • @LukeSkywalker 它不起作用。 a 在这种情况下是一个 numpy 数组,而不是一个列表,并且减号运算符会按元素进行减法。
【解决方案3】:

这是一个可能有帮助的 lil func:

def group_consecutives(vals, step=1):
    """Return list of consecutive lists of numbers from vals (number list)."""
    run = []
    result = [run]
    expect = None
    for v in vals:
        if (v == expect) or (expect is None):
            run.append(v)
        else:
            run = [v]
            result.append(run)
        expect = v + step
    return result

>>> group_consecutives(a)
[[0], [47, 48, 49, 50], [97, 98, 99]]
>>> group_consecutives(a, step=47)
[[0, 47], [48], [49], [50, 97], [98], [99]]

附:这是纯 Python。有关 NumPy 解决方案,请参阅 unutbu 的答案。

【讨论】:

  • P.S.如果你想要元组而不是列表,你可以做tuple(map(tuple, group_consecutives(a)))
  • 这不是 NumPy 解决方案!
  • 这应该比使用np.split的答案慢得多。
【解决方案4】:

这是我到目前为止提出的:不确定是否 100% 正确

import numpy as np
a = np.array([ 0, 47, 48, 49, 50, 97, 98, 99])
print np.split(a, np.cumsum( np.where(a[1:] - a[:-1] > 1) )+1)

返回:

>>>[array([0]), array([47, 48, 49, 50]), array([97, 98, 99])]

【讨论】:

  • 反例:a = np.array([ 0, 47, 48, 49, 50, 97, 98, 99, 101, 102, 103, 140, 141]) print(np.split (a, np.cumsum( np.where(a[1:] - a[:-1] > 1) )+1)) 产生 [array([0]), array([47, 48, 49, 50 ]), 数组([ 97, 98, 99, 101, 102, 103, 140]), 数组([141]), 数组([], dtype=int64)]
【解决方案5】:
def consecutive(data, stepsize=1):
    return np.split(data, np.where(np.diff(data) != stepsize)[0]+1)

a = np.array([0, 47, 48, 49, 50, 97, 98, 99])
consecutive(a)

产量

[array([0]), array([47, 48, 49, 50]), array([97, 98, 99])]

【讨论】:

  • 对于查找相同字符串的运行:partitions = np.where(a[1:] != a[:-1])[0] + 1np.diff 不适用于字符串)
  • 谢谢!如果我们使用np.split(np.r_[:len(data)], np.where(np.diff(data) != stepsize)[0]+1),我们会得到连续的索引列表,所以如果data 是一张大表的一列,我们可以使用该结果来索引相同的行组。
  • @unutbu 如果我只想选择具有至少 5 个连续元素的组,我可以知道在此函数中需要更改什么吗?
【解决方案6】:

针对一维数组进行测试

获取 diff 不是一的地方

diffs = numpy.diff(array) != 1

获取diff的索引,获取第一个维度并加一,因为diff与前一个索引比较

indexes = numpy.nonzero(diffs)[0] + 1

用给定的索引分割

groups = numpy.split(array, indexes)

【讨论】:

    【解决方案7】:

    事实证明,列表解析比 np.split 更具执行性。所以下面的函数(几乎像@unutbu's consecutive function,除了它使用列表解析来拆分数组)要快得多:

    def consecutive_w_list_comprehension(arr, stepsize=1):
        idx = np.r_[0, np.where(np.diff(arr) != stepsize)[0]+1, len(arr)]
        return [arr[i:j] for i,j in zip(idx, idx[1:])]
    

    例如,对于长度为 100_000 的数组,consecutive_w_list_comprehension 的速度提高了 4 倍以上:

    arr = np.sort(np.random.choice(range(150000), size=100000, replace=False))
    
    %timeit -n 100 consecutive(arr)
    96.1 ms ± 1.22 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %timeit -n 100 consecutive_w_list_comprehension(arr)
    23.2 ms ± 858 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    事实上,无论数组大小如何,这种关系都成立。下图显示了此处答案之间的运行时差异。

    用于生成上图的代码:

    import perfplot
    import numpy as np
    
    def consecutive(data, stepsize=1):
        return np.split(data, np.where(np.diff(data) != stepsize)[0]+1)
    
    def consecutive_w_list_comprehension(arr, stepsize=1):
        idx = np.r_[0, np.where(np.diff(arr) != stepsize)[0]+1, len(arr)]
        return [arr[i:j] for i,j in zip(idx, idx[1:])]
    
    def group_consecutives(vals, step=1):
        run = []
        result = [run]
        expect = None
        for v in vals:
            if (v == expect) or (expect is None):
                run.append(v)
            else:
                run = [v]
                result.append(run)
            expect = v + step
        return result
    
    
    def JozeWs(array):
        diffs = np.diff(array) != 1
        indexes = np.nonzero(diffs)[0] + 1
        groups = np.split(array, indexes)
        return groups
    
    perfplot.show(
        setup = lambda n: np.sort(np.random.choice(range(2*n), size=n, replace=False)),
        kernels = [consecutive, consecutive_w_list_comprehension, group_consecutives, JozeWs],
        labels = ['consecutive', 'consecutive_w_list_comprehension', 'group_consecutives', 'JozeWs'],
        n_range = [2 ** k for k in range(5, 22)],
        equality_check = lambda *lst: all((x==y).all() for x,y in zip(*lst)),
        xlabel = '~len(arr)'
    )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多