Python 使用矢量化查找最大连续元素数

Python 使用矢量化查找最大连续元素数,python,numpy,vectorization,Python,Numpy,Vectorization,作为我项目的一部分,我需要找出向量中是否有4个或更多的连续元素,以及它们的索引。目前我正在使用以下代码: #sample arrays: #a1 = np.array([0, 1, 2, 3, 5]) #a2 = np.array([0, 1, 3, 4, 5, 6]) #a3 = np.array([0, 1, 3, 4, 5]) a4 = array([0, 1, 2, 4, 5, 6]) dd = np.diff(a4) #array([1, 1, 2, 1, 1]) c = 0 idx

作为我项目的一部分,我需要找出向量中是否有4个或更多的连续元素,以及它们的索引。目前我正在使用以下代码:

#sample arrays:
#a1 = np.array([0, 1, 2, 3, 5])
#a2 = np.array([0, 1, 3, 4, 5, 6])
#a3 = np.array([0, 1, 3, 4, 5])
a4 = array([0, 1, 2, 4, 5, 6])

dd = np.diff(a4) #array([1, 1, 2, 1, 1])
c = 0
idx = []
for i in range(len(dd)):
    if dd[i]==1 and c<3:
        idx.append(i)
        c+=1
    elif dd[i]!=1 and c>=3:
        break
    else:
         c=0
         idx=[]
#示例数组:
#a1=np.数组([0,1,2,3,5])
#a2=np.数组([0,1,3,4,5,6])
#a3=np.数组([0,1,3,4,5])
a4=数组([0,1,2,4,5,6])
dd=np.diff(a4)#数组([1,1,2,1,1])
c=0
idx=[]
对于范围内的i(len(dd)):
如果dd[i]==1且c=3:
打破
其他:
c=0
idx=[]

我很想看看是否可以避免for循环,只使用numpy函数来完成这项任务。

是的。您启动正确:

from numpy import array, diff, where

numbers = array([0, 1, 3, 4, 5, 5])
differences = diff(numbers)
您对连续数字感兴趣:

consecutives = differences == 1
你想要两个连续的箱子。可以将阵列与其偏移量进行比较:

(consecutives[1:] & consecutives[:-1]).any()
#>>> True
要获取发生次数,请使用
.sum()
而不是
.any()

如果需要索引,只需使用
numpy.where

[offset_indexes] = where(consecutives[1:] & consecutives[:-1])
offset_indexes
#>>> array([2])
编辑:您似乎已将所需长度从
3
编辑为
4
。这将使我的代码无效,但您只需要设置

consecutives[1:] & consecutives[:-1]


以下是一个毫无意义的通用版本:

from numpy import arange, array, diff, where

def doubling_step_shifts(shifts):
    """
    When you apply a mask of some kind of all rotations,
    often the size of the last prints will allow shifts
    larger than 1. This is a helper for that.

    A mask is assumed to exist before invocation, as
    this is typically called repeatedly on the mask or
    a copy.
    """
    # Total shift
    subtotal = 1
    step = 1

    # While the shifts won't overflow
    while subtotal + step < shifts:
        yield step
        subtotal += step
        step *= 2

    # Make up the remainder
    if shifts - subtotal > 0:
        yield shifts - subtotal

def consecutive_indexes_of_length(numbers, length):
    # Constructing "consecutives" creates a
    # minimum mask of 1, whereas this would need
    # a mask of 0, so we special-case these
    if length <= 1:
        return arange(numbers.size)

    # Mask of consecutive numbers
    consecutives = diff(numbers) == 1
    consecutives.resize(numbers.size)

    # Recursively reapply mask to cover lengths too short
    for i in doubling_step_shifts(length-1):
        consecutives[:-i] &= consecutives[i:]

    # Reextend those lengths
    for i in doubling_step_shifts(length):
        consecutives[i:] = consecutives[i:] | consecutives[:-i]

    # Give the indexes
    return where(consecutives)[0]
为什么??没有理由。它是
O(n log k)
其中
n
是列表中的元素数,
k
GROUPSIZE
,所以不要将它用于大型
GROUPSIZE
。然而,对于几乎所有的团队规模来说,它应该是相当快的

编辑:现在速度相当快。我打赌Cython会快一点,但这没问题


这种实现的优点是相对简单、可扩展并且使用非常原始的操作。除了非常小的输入,这可能不会比Cython循环快。

下面的递归解决方案怎么样?(当然,仅适用于1dim阵列)

我觉得它优雅得让人恶心。我不是说你应该用它,但我想起来很开心

import numpy as np

def is_consecutive(arr, n):
    if n <= len(arr) <= 1:
        return True
    if len(arr) < n:
        return False
    diffs1idx = np.where(np.diff(arr) == 1)[0]
    return is_consecutive(diffs1idx, n-1)

print is_consecutive([1,2], 3)  # False
print is_consecutive([1,2,3], 3)  # True
print is_consecutive([5,1,2,3], 3)  # True
print is_consecutive([4,9,1,5,7], 3)  # False
print is_consecutive([4,9,1,2,3, 7, 9], 3)  # True
print is_consecutive(np.arange(100), 100)  # True
print is_consecutive(np.append([666], np.arange(100)), 100)  # True
print is_consecutive(np.append([666], np.arange(100)), 101)  # False
将numpy导入为np
def是连续的(arr,n):

如果n这将为您提供一个包含所有连续块长度的数组:

np.diff(np.concatenate(([-1],) + np.nonzero(np.diff(a) != 1) + ([len(a)-1],)))
一些测试:

numbers = array([1, 2, 3, 4, 5, 6, 9, 10, 11, 14, 17, 18, 19, 20, 21])

consecutive_indexes_of_length(numbers, 1)
#>>> array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
consecutive_indexes_of_length(numbers, 2)
#>>> array([ 0,  1,  2,  3,  4,  5,  6,  7,  8, 10, 11, 12, 13, 14])
consecutive_indexes_of_length(numbers, 3)
#>>> array([ 0,  1,  2,  3,  4,  5,  6,  7,  8, 10, 11, 12, 13, 14])
consecutive_indexes_of_length(numbers, 4)
#>>> array([ 0,  1,  2,  3,  4,  5, 10, 11, 12, 13, 14])
consecutive_indexes_of_length(numbers, 5)
#>>> array([ 0,  1,  2,  3,  4,  5, 10, 11, 12, 13, 14])
consecutive_indexes_of_length(numbers, 6)
#>>> array([0, 1, 2, 3, 4, 5])
consecutive_indexes_of_length(numbers, 7)
#>>> array([], dtype=int64)
>>> a = [1, 2, 3, 4, 5, 6, 9, 10, 11, 14, 17, 18, 19, 20, 21]
>>> np.diff(np.concatenate(([-1],) + np.nonzero(np.diff(a) != 1) +
                           ([len(a)-1],)))
array([6, 3, 1, 5], dtype=int64)

>>> a = [0, 1, 2, 4, 5, 6]
>>> np.diff(np.concatenate(([-1],) + np.nonzero(np.diff(a) != 1) +
                           ([len(a)-1],)))
array([3, 3], dtype=int64)
要检查是否有至少4个项目长,请将上述代码简单地包装在
np.any(…>=4)


为了了解这是如何工作的,让我们从内到外为我的第一个示例计算结果:

>>> a = [1, 2, 3, 4, 5, 6, 9, 10, 11, 14, 17, 18, 19, 20, 21]
首先,我们计算出连续项之间的差值:

>>> np.diff(a)
array([1, 1, 1, 1, 1, 3, 1, 1, 3, 3, 1, 1, 1, 1])
然后,我们确定增量不是
1
的位置,即连续项块开始或结束的位置:

>>> np.diff(a) != 1
array([False, False, False, False, False,  True, False, False,  True,
        True, False, False, False, False], dtype=bool)
我们提取
True
s的位置:

>>> np.nonzero(np.diff(a) != 1)
(array([5, 8, 9], dtype=int64),)
上面的索引标记了连续条纹中的最后一个项目。Python切片定义为
start
last+1
,因此我们可以将该数组增加1,在开始处添加0,在结束处添加数组长度,并具有连续序列的所有开始和结束索引,即:

>>> np.concatenate(([0], np.nonzero(np.diff(a) != 1)[0] + 1, [len(a)]))
array([ 0,  6,  9, 10, 15], dtype=int64)
从连续索引中提取差异将为我们提供每个连续块的所需长度。因为我们关心的只是差异,而不是在索引中添加差异,所以在我最初的回答中,我选择在
-1
前面加上
len(a)-1

>>> np.concatenate(([-1],) + np.nonzero(np.diff(a) != 1) + ([len(a)-1],))
array([-1,  5,  8,  9, 14], dtype=int64)
>>> np.diff(np.concatenate(([-1],) + np.nonzero(np.diff(a) != 1) +
                           ([len(a)-1],)))
array([6, 3, 1, 5], dtype=int64)
假设在此数组中,您确定需要
5
项块的索引,即位于该数组
3
位置的索引。要恢复该块的开始和停止索引,只需执行以下操作:

>>> np.concatenate(([0], np.nonzero(np.diff(a) != 1)[0] + 1, [len(a)]))[3:3+2]
array([10, 15], dtype=int64)
>>> a[10:15]
[17, 18, 19, 20, 21]

看到这个解决方案很有趣,但我想它在python中不是很有效,因为python函数调用很昂贵。谢谢。你能解释一下它是怎么工作的吗?我还需要这些元素的索引。它应该输出原始矩阵中的索引,即[2,3,4]抱歉,但不是真的!你也能给我一个关于&part的小解释吗?我有点忙。但是
offset\u index
将为每个索引提供起始索引,因此
offset\u index+1
将为您提供中间索引,
offset\u index+2
将为您提供最后一个索引。如果您想要这些元素的并集:创建一个原始大小的空数组
X
,让
C=continuetives[1:]&continuetives[:-1]
,并将
C
0
1
2
的移位添加到
X
。然后在
X
上调用
numpy.where
。如果有效,请随时更新我的答案:)。
>>> np.concatenate(([0], np.nonzero(np.diff(a) != 1)[0] + 1, [len(a)]))[3:3+2]
array([10, 15], dtype=int64)
>>> a[10:15]
[17, 18, 19, 20, 21]