3
\$\begingroup\$

I have a list of segment lengths and I want to convert it into array slices:

segment_lens = [2, 5, 5]

last_end = 0
start = 0
end = 0

for s_i in range(len(segment_lens)):
    start = last_end
    end += segment_lens[s_i]
    print("Slice: [%s,%s]" %(start, end,))
    last_end = end

Here is the intended output, which in my application I would use to slice a list:

Slice: [0,2]
Slice: [2,7]
Slice: [7,12]

Is there a better way to do this? I feel like there should be a python built-in for this, but I'm not sure.

\$\endgroup\$
4
\$\begingroup\$

I would probably use iter for this:

def get_chunks(l, chunk_lengths):
    it = iter(l)
    for chunk_length in chunk_lengths:
        yield [next(it) for _ in range(chunk_length)]

Usage:

>>> get_chunks(range(100), [1,2,5])
<generator object get_chunks at 0x7f2744f68910>

>>> list(get_chunks(range(100), [1,2,5]))
[[0], [1, 2], [3, 4, 5, 6, 7]]

Review:

I would make your code at least a function that yeilds the slices and give it a nice name, like I did in the function above.

Note that it is frowned upon in python to iterate over the indices of a list, if all you need are the elements. So prefer this:

for item in l:
    print(item)

over

for i in range(len(l)):
    print(l[i])

Also note that using "%s" % var style formatting is the old way of doing it. It is recommended to use str.format, which offers a lot more possibilities (none of which you need here, though).

print("Slice: [{},{}]".format(start, end,))
\$\endgroup\$
2
\$\begingroup\$

Coming from a focus on numpy, a logical solution is:

In [41]: idx = [0] + [2,5,5]
In [42]: idx = np.cumsum(idx)
In [43]: idx
Out[43]: array([ 0,  2,  7, 12], dtype=int32)
In [44]: slices=[slice(idx[i],idx[i+1]) for i in range(len(idx)-1)]
In [45]: slices
Out[45]: [slice(0, 2, None), slice(2, 7, None), slice(7, 12, None)]

The cumsum could also be implemented with itertools.accumulate:

In [46]: import operator
In [47]: import itertools
In [48]: idx = [0] + [2,5,5]
In [49]: idx = list(itertools.accumulate(idx, operator.add))
In [50]: idx
Out[50]: [0, 2, 7, 12]

Though a generator makes as much sense:

def cumsum(alist):
    accum = 0
    for i in alist:
        yield accum
        accum += i
    yield accum

In [58]: list(cumsum([2,5,5]))
Out[58]: [0, 2, 7, 12]

An all-in-one generator would be:

def slices(alist):
    start, end = 0, 0
    for i in alist:
        end += i
        yield slice(start, end)
        start = end

In [65]: list(slices([2,5,5]))
Out[65]: [slice(0, 2, None), slice(2, 7, None), slice(7, 12, None)]

But I prefer the logic of calculating the cumsum first, and the slices from that.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.