4
\$\begingroup\$

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)] 
\$\endgroup\$
3
  • 5
    \$\begingroup\$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\$\endgroup\$CommentedJun 12, 2018 at 12:46
  • 1
    \$\begingroup\$@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'\$\endgroup\$
    – Jack
    CommentedJun 13, 2018 at 0:55
  • \$\begingroup\$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?\$\endgroup\$CommentedJun 13, 2018 at 7:46

3 Answers 3

8
\$\begingroup\$

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 def advance(iterator, step): next(itertools.islice(iterator, step, step), None) def tuplewize(iterable, size): iterators = itertools.tee(iterable, size) for position, iterator in enumerate(iterators): advance(iterator, position) 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:

  • numpy.roll allows us to advance the nth element on top of the list;
  • numpy.stack allows us to concatenate the rolled arrays into a single 2D array;
  • numpy.transpose allows us to convert a "list of lists" into a "list of tuples".

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.

\$\endgroup\$
2
  • 3
    \$\begingroup\$There is a package that can replace tuplewize, more_itertools.windowed\$\endgroup\$
    – Peilonrayz
    CommentedJun 12, 2018 at 14:39
  • \$\begingroup\$@Peilonrayz your answer is great.\$\endgroup\$
    – Jack
    CommentedJun 13, 2018 at 1:12
1
\$\begingroup\$

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 ) ] 
\$\endgroup\$
    1
    \$\begingroup\$

    Comments about your code in no particular order:

    • 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.

    Code after my comments:

    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)] 
    \$\endgroup\$
    2

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.