I'm looking for a general method on how to reverse a slice in Python.
I read this comprehensive post, which has several nice explanations about how slicing works:
Understanding Python's slice notation
Yet I cannot figure out a generalized rule on how to calculate a reversed slice which addresses exactly the same elements in reverse order. I was actually surprised not to find a builtin method doing this.
What I'm looking for is a method reversed_slice
that works like this with arbitrary start
, stop
and step
values including negative values:
>>> import numpy as np
>>> a = np.arange(30)
>>> s = np.s_[10:20:2]
>>> a[s]
array([10, 12, 14, 16, 18])
>>> a[reversed_slice(s,len(a))]
array([18, 16, 14, 12, 10])
What I've tried but doesn't work is this:
def reversed_slice(slice_, len_):
"""
Reverses a slice (selection in array of length len_),
addressing the same elements in reverse order.
"""
assert isinstance(slice_, slice)
instart, instop, instep = slice_.indices(len_)
if instep > 0:
start, stop, step = instop - 1, instart - 1, -instep
else:
start, stop, step = instop + 1, instart + 1, -instep
return slice(start, stop, step)
This works fine for step of 1
and when the last addressed element coincides with stop-1
. For other cases it does not:
>>> import numpy as np
>>> a = np.arange(30)
>>> s = np.s_[10:20:2]
>>> a[s]
array([10, 12, 14, 16, 18])
>>> a[reversed_slice(s,len(a))]
array([19, 17, 15, 13, 11])
So it seems like I'm missing some relation like (stop - start) % step
.
Any help on how to write a general method is greatly appreciated.
Notes:
I do know that there a other possibilities to get a sequence with the same elements reversed, like calling
reversed(a[s])
. This is not an option here, as I need to reverse the slice itself. The reason is that I work onh5py
datasets which do not allow negativestep
values in slices.An easy but not very elegant way would be the use of coordinate lists, i.e.
a[list(reversed(range(*s.indices(len(a)))))]
. This is also not an option due to theh5py
requirement that indices in the list must be given in increasing order.
Answer
I just wanted to use the answer to this question but while testing found that there are still some cases that would silently give the wrong result -
The following definition of the reversed_slice function developed from the other answers seems to cover those cases correctly -
def reversed_slice(s, len_):
"""
Reverses a slice selection on a sequence of length len_,
addressing the same elements in reverse order.
"""
assert isinstance(s, slice)
instart, instop, instep = s.indices(len_)
if (instop < instart and instep > 0) or (instop > instart and instep < 0) \
or (instop == 0 and instart == 0) :
return slice(0,0,None)
overstep = abs(instop-instart) % abs(instep)
if overstep == 0 :
overstep = abs(instep)
if instep > 0:
start = instop - overstep
stop = instart - 1
else :
start = instop + overstep
stop = instart + 1
if stop < 0 :
stop = None
return slice(start, stop, -instep)
No comments:
Post a Comment