# Slice a list or NumPy array into consecutive tuples

My function needs two params(a,b) and if...break, which seems redundant. Can it be made prettier?

def slice_list(lst,num):
"""
Given a list or Numpy array，
get consecutive tuples based on specific window.
"""
rst = []
for i,_ in enumerate(lst):
a = i
b = i + num
if len(lst[a:b])<num:
break
rst.append(tuple(lst[a:b]))
return rst


Example:

>>> lst = np.array([10, 11, 12, 13, 26, 28])
>>> slice_list(lst,4)
[(10, 11, 12, 13), (11, 12, 13, 26), (12, 13, 26, 28)]

• This question is incomplete. To help reviewers give you better answers, please add sufficient context to your question. The more you tell us about what your code does and what the purpose of doing that is, the easier it will be for reviewers to help you. Questions should include a description of what the code does Jun 12, 2018 at 12:46
• @MathiasEttinger I don't know why my code should give more context. What did I miss? I think others answers get my point. And I open your link but get 'Site not found'
– Jack
Jun 13, 2018 at 0:55
• Sorry, the link should be codereview.meta.stackexchange.com/q/1226/84718 Basically you don't explain the requirements of your function and thus we are left to guess at the specifications from the code and the example. How can we know that the code is correctly implemented, then? Jun 13, 2018 at 7:46

I believe that what you are looking for is already available as an itertools recipe; even though pairwise only allow you to return couples and not tuple of arbitrary length.

You will need to modify it so that:

• tee will return num iterators;
• you advance each of these iterators by one more element than the previous one (see the consume recipe for that).

This can lead to the following code:

import itertools

next(itertools.islice(iterator, step, step), None)

def tuplewize(iterable, size):
iterators = itertools.tee(iterable, size)
for position, iterator in enumerate(iterators):
return zip(*iterators)


Usage being:

>>> for t in tuplewize(lst, 4):
...   print(t)
...
(10, 11, 12, 13)
(11, 12, 13, 26)
(12, 13, 26, 28)


However, you are using numpy so we may come up with a better numpy approach:

Full code being:

import numpy as np

def tuplewize(array, size):
if size < 2:
return np.array([array])

stack = np.stack([np.roll(array, -i) for i in range(size)])
return np.transpose(stack)[:-size+1]


Usage being:

>>> tuplewise(lst, 4)
array([[10, 11, 12, 13],
[11, 12, 13, 26],
[12, 13, 26, 28]])


And as @Peilonrayz indicated in a comment, a third possibility is to use the more_itertools package and its windowed function which has extended capabilities; and as such more overhead, so depending on your needs it may or may not suit you.

• There is a package that can replace tuplewize, more_itertools.windowed Jun 12, 2018 at 14:39
– Jack
Jun 13, 2018 at 1:12

If you are passing in a list of 6 items, and you want sublists of length 4, the last list you can create starts at index 6-4=2. Instead of checking if the sublist is long enough, and breaking if it isn’t, generate only the valid list of starting indices:

for i in range( len(lst) - num + 1 ):
rst.append( tuple( lst[i:i+num] ) )


Depending on your definition of pretty, and personal aesthetics:

rst = [ tuple( lst[i:i+num] ) for i in range( len(lst) - num + 1 ) ]


• You throw away a value from enumerate, I would instead use range.
• You do an if statement every time you loop and because of this you need the a and b variables.
• I think size is a better variable name then num.

def slice_list2(lst,size):
if size > len(lst):
return []
rst = []
for i in range(0,len(lst)-size+1):
rst.append(tuple(lst[i:i+size]))
return rst


But I think this is a optimal place to use list comprehensions. So I would implement it something like this:

def slice_list3(lst,size):
return [tuple(lst[x:x+size]) for x in range(0,len(lst)-size+1)]