14

I have a string with digits like so - digit = "7316717"

Now I want to split the string in such a way that the output is a moving window of 3 digits at a time. So I get -

["731", "316", "167", "671", "717"]

How would the approach be? Straightforward way is to put in for-loop and iterate. But I feel some inbuilt python string function can do this in less code. Know of any such approach?

Srikar Appalaraju
  • 71,928
  • 54
  • 216
  • 264

5 Answers5

14

The itertools examples provides the window function that does just that:

from itertools import islice
def window(seq, n=2):
    "Returns a sliding window (of width n) over data from the iterable"
    "   s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ...                   "
    it = iter(seq)
    result = tuple(islice(it, n))
    if len(result) == n:
        yield result    
    for elem in it:
        result = result[1:] + (elem,)
        yield result

Example usage:

>>> ["".join(x) for x in window("7316717", 3)]
['731', '316', '167', '671', '717']
Shawn Chin
  • 84,080
  • 19
  • 162
  • 191
  • 1
    window? I use Python 2.6.1, in my itertools implementation I dnt see window. am i mising something here? – Srikar Appalaraju Oct 03 '11 at 14:01
  • My apologies for the confusion. It's not part of itertools. The `window` function shown above (taken from the linked doc) is an example of how to use `islice()` to create a generic `window` function. – Shawn Chin Oct 03 '11 at 14:04
  • There is a much more pleasant code in `itertools` [recipes](http://docs.python.org/library/itertools.html#recipes) called **pairwise**. – ovgolovin Oct 03 '11 at 14:27
  • 3
    `pairwise` does look more pleasant, but it's hardcoded to a sliding window of 2 (hence *pair*wise). I suppose it could be parametrised, but then it may not be as pleasant anymore and since it uses `tee` and `zip` it may not be as efficient as the `window` recipe. – Shawn Chin Oct 03 '11 at 14:40
  • `tee` and `izip` are written in C, so they are quite fast. The pleasantness is a matter of personal taste, I think :) – ovgolovin Oct 03 '11 at 15:02
12
>>> s = "7316717"
>>> [s[i:i+3] for i in range(len(s)-2)]
['731', '316', '167', '671', '717']
Steven Rumbalski
  • 44,786
  • 9
  • 89
  • 119
3

There is a very good recipe pairwise in itertools docs.

Modernizing it a bit for n elements in the group, I made this code:

from itertools import tee, izip

def window(iterable, n):
    els = tee(iterable, n)
    for i, el in enumerate(els):
        for _ in xrange(i):
            next(el, None)
    return izip(*els)


print(["".join(i) for i in window("2316515618", 3)])

Python 2.7

ovgolovin
  • 13,063
  • 6
  • 47
  • 78
  • You're right. It is arguable more pleasant than the islice version. Clever use of `tee` and `next`. +1 – Shawn Chin Oct 03 '11 at 16:34
  • Same solution as proposed here: http://stackoverflow.com/a/6822907/145400 Thanks to both of you. I like @ovgolovin's `enumerate` version more. You should, however, spend some more spaces and maybe use `xrange` instead of `range`. – Dr. Jan-Philip Gehrcke Aug 08 '13 at 15:03
  • @Jan-PhilipGehrcke Please, edit my answer as you feel like. StackOverflow is a collaborative work, which may help other people in the future! – ovgolovin Aug 08 '13 at 15:07
0

Following Shawn's, the newest example of sliding window uses collections.deque:

def sliding_window(iterable, n):
    # sliding_window('ABCDEFG', 4) -> ABCD BCDE CDEF DEFG
    it = iter(iterable)
    window = collections.deque(islice(it, n), maxlen=n)
    if len(window) == n:
        yield tuple(window)
    for x in it:
        window.append(x)
        yield tuple(window)
funnydman
  • 9,083
  • 4
  • 40
  • 55
0
digit = "7316717"
digit_sets = [digit[i:i+3] for i in range(len(digit)-2)]
Amber
  • 507,862
  • 82
  • 626
  • 550