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

Viewed 67

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.

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.

3 Answers

To satisfy the error tolerance, I maintained a rotating buffer of hits and near-misses and checked how many we had. For simplicity this is just coded as a straight run through all values in the array.

def findFLseq(values, diff, sqlen, errtol, errqy):
    difflen = sqlen-1
    startseq = None
    endseq = None
    prev = -(diff+errtol+1)
    seqstate = -1 # => prev not valid
    errbuf = [sqlen]*difflen
    for i, val in enumerate(values):
        adjdiff = val - prev - diff
        if adjdiff == 0:
            seqstate += 1
            errtag = 0
        elif abs(adjdiff) <= errtol:
            seqstate += 1
            errtag = 1
        else:
            seqstate = 0
            errtag = sqlen
        errbuf[i%difflen] = errtag
        if seqstate >= difflen and sum(errbuf) <= errqy:
            endseq = i
            if startseq == None:
                startseq = i-difflen
        prev = val
    
    if startseq == None:
        return (None,None)
    else:
        return (values[startseq], values[endseq])

You could try this:

a = [...]
b = []
x = 0
c = True
max = 5
# I use this while loop to catch when we reach the end of the list, while also catching
# when we are finished with our desired output of max.
while c == True:
    try:
        if a[x] == a[x+1]-1:
            b.append(a[x])
            if len(b) == max:
            # Once we reach desired length we end the while loop with c = False
                c = False
        else:
            b = []
            # If we get to a number that does not follow sequentially, 
            # then we reset the list
    except:
        # Once we get to the end of the list with x, we stop the loop.
        c = False
    # Iterate the next number in the list
    x += 1

print(b)

The code might not be the cleanest, but it does what you seem to need happen. The test list I used is [2,3,4,4,5,6,7,8,9,10,2,3,4] and it provided the resulting list [4,5,6,7,8].

With some small alterations it can be used to go backwards as well with:

# Make sure to redefine your starting values
b = []
x = 0
c = True
while c == True:
    try:
        if a[-1-x] == a[-1-(x+1)]+1:
            b.append(a[-1-x])
            if len(b) == max:
                c = False
        else:
            b = []
    except:
        c = False
    x += 1
print(b)

Which gives me [10,9,8,7,6]. I wasn't sure what your error_difference or accepted meant completely so I don't have anything for that.

Sometimes a good way to approach such a question is to answer a more general question and then derive the special case you need.

Here's an idea: for each position in your input array, compute the number of following consecutive numbers. This can be done by looping backwards over the indices:

import numpy as np

a = np.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])

n_followers = np.repeat(0, repeats=len(a))  # initialize with zeros

# loop backwards, starting from the next to last index of a
for i in np.arange(start=len(a)-2, stop=-1, step=-1):
    if a[i] + 1 == a[i + 1]:  # if a[i] is followed by the consecutive number
        n_followers[i] = n_followers[i + 1] + 1 # then it has one more 
                                                # consecutively following number
                                                # than its following number  
n_followers
array([ 0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  0,  7,  6,  5,  4,  3,  2,
        1,  0,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,  5,  4,  3,  2,  1,
        0, 18, 17, 16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,
        2,  1,  0, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22,
       21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,
        4,  3,  2,  1,  0,  1,  0, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,
        2,  1,  0, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,  9,  8,  7,
        6,  5,  4,  3,  2,  1,  0,  1,  0])

By the way, when you say

I need to find the first number from where its sequential in order for up-to 5 digits,

I suppose you mean "for at least 5", right?

We can easily get that number now:

min_index = np.min(np.arange(len(a))[n_followers >= 5])
a[min_index]
54

You can of course adapt this approach to the backward question, and including the relaxed error allowances is also straightforward. I hope this helps you to get started.

Related