How to get start point of numbers in sequence and handle errors?

Viewed 109

Let me start with an example,

array([  1,   6,  12,  14,  16,  18,  21,  23,  24,  27,  29,  54,  55,
        56,  57,  58,  59,  60,  61,  63,  64,  65,  66,  67,  68,  69,
        70,  71,  72,  74,  75,  76,  77,  78,  79,  81,  82,  83,  84,
        85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,
        98,  99, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
       112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
       125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 138,
       139, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
       153, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
       167, 168, 169, 170, 171, 172, 173, 174, 175, 177, 178])

Ideal Output: 54 (forward), 175 (backward)

Conditions to satisfy :

I need to find the first number from where its sequential in order for up-to 5 digits, for ex: from 54 onward the numbers are sequential in order (i.e., 54, 55, 56, 57, 58) (5 digits).

I require the same in reverse order (i., 175, 174, 173, 172, 171) (5 digits) from point 175 its sequential in order.

Update: I forgot to mention, in some cases I need a variable to assign the error that is acceptable.

For example, lets say the error_accepted = 1 and error_difference(upto) = 1, then if the values are 54, 56, 57, 58, 59 can be accepted. The output is still 54. Else, if error_accepted = 2 and error_difference (upto) = 3, then if the values are 54, 58, 59, 61, 62 can also be accepted. The output is still 54.

I tried to do something, the logic is not correct so please do excuse the code I have written.

# excuse the code, the logic ain't correct. 
start_ix_cnt = 0
start_ix_indices = None

counter_start = 0
counter = 0

for ix in matching_indexes:
    if start_ix_indices is None and start_ix_cnt == 0:
        start_ix_indices = list(range(1, 178))
        start_ix_cnt += 1
    else:
        if ix == start_ix_indices[start_ix_cnt]:
            counter += 1
            counter_start = start_ix_indices[0]
        else:
            start_ix_indices = list(range(ix, 178))
            start_ix_cnt = 0

Any help is appreciated.

4 Answers

Using Numpy you can split your array into sequential sets and find the sequence you're looking for using the length of set and/or its items:

In [96]: mask = np.diff(arr) != 1

In [97]: np.split(arr, np.where(mask)[0] + 1)
Out[97]: 
[array([1]),
 array([6]),
 array([12]),
 array([14]),
 array([16]),
 array([18]),
 array([21]),
 array([23, 24]),
 array([27]),
 array([29]),
 array([54, 55, 56, 57, 58, 59, 60, 61]),
 array([63, 64, 65, 66, 67, 68, 69, 70, 71, 72]),
 array([74, 75, 76, 77, 78, 79]),
 array([81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
        98, 99]),
 array([101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
        114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
        127, 128, 129, 130, 131, 132, 133, 134, 135, 136]),
 array([138, 139]),
 array([141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153]),
 array([155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
        168, 169, 170, 171, 172, 173, 174, 175]),
 array([177, 178])]

You can use following function in order to handle the accepted margin error:

In [98]: def sequencer(arr, err=1):
    ...:     mask = np.diff(arr) > err
    ...:     return np.split(arr, np.where(mask)[0] + 1)
# forward
for i in range(len(a)-5):
    if a[i+5] - a[i] <= 5 + upto:
        print(a[i])
        break

# reverse
for i in range(len(a)-1, 5, -1):
    if a[i] - a[i-5] <= 5 + upto:
        print(a[i])
        break

This is my approach to it.

x = [  1,   6,  12,  14,  16,  18,  21,  23,  24,  27,  29,  54,  55,
        56,  57,  58,  59,  60,  61,  63,  64,  65,  66,  67,  68,  69,
        70,  71,  72,  74,  75,  76,  77,  78,  79,  81,  82,  83,  84,
        85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,
        98,  99, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
       112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
       125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 138,
       139, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
       153, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
       167, 168, 169, 170, 171, 172, 173, 174, 175, 177, 178]
groups,count = [],0
def checkConsecutive(l):return (l == list(range(min(l), max(l)+1)) or l.reverse() == list(range(min(l), max(l)+1))) and len(l) == 5
for numbers in x:
    groups.append(x[count:count+5])
    count += 1
check = lambda l:(l == list(range(min(l), max(l)+1)) or l.reverse() == list(range(min(l), max(l)+1))) and len(l) == 5
newGroup = [seq for seq in groups if check]
print(newGroup)


If code simplicity is important (rather than raw speed), you could write a function and call it twice, once with the test list and once with the reversed test list. This version is general purpose for required difference and length of sequence of such differences:

def findseq(values, diff, threshold):
    seqstart = None
    prev = 0
    seqstate = -1 # => prev not valid
    for val in values:
        if val - prev == diff:
            if seqstate == 0:
                seqstart = prev
            seqstate += 1
            if seqstate == threshold:
                break
        else:
            seqstate = 0
        prev = val
        
    return seqstart

print(findseq(test_array,1,5))
print(findseq(reversed(test_array),-1,5))
Related