Zakum
Zakum

Reputation: 2287

Pythonic: Find all consecutive sub-sequences of certain length

I have a list of integers and I want to find all consecutive sub-sequences of length n in this list. For example:

>>> int_list = [1,4,6,7,8,9]
>>> conseq_sequences(int_list, length=3)
[[6,7,8], [7,8,9]]

The best I could come up with is:

def conseq_sequences(self, li, length):
    return [li[n:n+length]
            for n in xrange(len(li)-length+1)
            if li[n:n+length] == range(li[n], li[n]+length)]

This isn't overly readable. Is there any readable pythonic way of doing this?

Upvotes: 3

Views: 2896

Answers (4)

Pranav Raj
Pranav Raj

Reputation: 873

 def condition (tup):
    if tup[0] + 1 == tup[1] and tup[1] + 1 == tup[2] :
        return True
    return False

 def conseq_sequence(li):
   return [x for x in map(None, iter(li), iter(li[1:]), iter(li[2:])) if condition(x)]

Upvotes: 0

Padraic Cunningham
Padraic Cunningham

Reputation: 180441

Using operator.itemgetter and itertools.groupby

 def conseq_sequences(li, length):
    res = zip(*(li[i:] for i in xrange(length)))
    final = []
    for x in res:
        for k, g in groupby(enumerate(x), lambda (i, x): i - x):
            get_map = map(itemgetter(1), g)
            if len(get_map) == length:
                final.append(get_map)
    return final

Without imports.

def conseq_sequences(li, length):
    res = zip(*(li[i:] for i in xrange(length)))
    final = []
    for ele in res:
        if all(x == y+1 for x, y in zip(ele[1:], ele)):
            final.append(ele)
    return final

Which can be turned into list comprehension:

def conseq_sequences(li, length):
    res = zip(*(li[i:] for i in xrange(length)))
    return [ ele for ele in res if all(x == y+1 for x, y in zip(ele[1:], ele))]

Upvotes: 0

jfs
jfs

Reputation: 414395

Here's a more general solution that works for arbitrary input iterables (not just sequences):

from itertools import groupby, islice, tee
from operator import itemgetter

def consecutive_subseq(iterable, length):
    for _, consec_run in groupby(enumerate(iterable), lambda x: x[0] - x[1]):
        k_wise = tee(map(itemgetter(1), consec_run), length)
        for n, it in enumerate(k_wise):
            next(islice(it, n, n), None) # consume n items from it
        yield from zip(*k_wise)

Example:

print(*consecutive_subseq([1,4,6,7,8,9], 3))
# -> (6, 7, 8) (7, 8, 9)

The code uses Python 3 syntax that could be adapted for Python 2 if needed.

See also, What is the most pythonic way to sort dates sequences?

Upvotes: 3

Marcin
Marcin

Reputation: 238309

One solution could be as follows:

import numpy # used diff function from numpy, but if not present, than some lambda or other helper function could be used. 

def conseq_sequences(li, length):
    return [int_list[i:i+length] for i in range(0, len(int_list)) if sum(numpy.diff(int_list[i:i+length]))==length-1]

Basically, first, I get consecutive sub-lists of given length from the list, and then check if the sum of the differences of their elements is equal to length - 1.

Please not that if elements are consecutive, their difference will add up to length - 1, e.g. for sub-list [5,6,7] the difference of its elements is [1, 1] and sum of it is 2.

But to be honest not sure if this solution is clearer or more pythonic than yours.

Just in case you don't have numpy, the diff function can be easly defined as follows:

def diff(l):
  '''For example, when l=[1,2,3] than return is [1,1]'''  
  return [x - l[i - 1] for i, x in enumerate(l)][1:]

Upvotes: 1

Related Questions