Python 使用矢量化查找最大连续元素数
作为我项目的一部分,我需要找出向量中是否有4个或更多的连续元素,以及它们的索引。目前我正在使用以下代码: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
#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]