Alex Deft
Alex Deft

Reputation: 2787

How to slice starting from negative to a positive index or the opposite

Numpy cannot perform the following indexing:

a = np.arange(10)
a[-2: 2]

I'm doing it in a not very elegant way at the moment, is there a trick or oneliner to get that?

Notice that I don't know if I'm facing this scenario in my code, it does happen sometimes, so I'm looking for a dynamic and one-for-all solution, not something for this exact case only.

My generalized slicer, quite long.

def slicer(array, lower_, upper_):
    n = len(array)
    lower_ = lower_ % n  # if negative, you get the positive equivalent. If > n, you get principal value.
    roll = lower_
    lower_ = lower_ - roll
    upper_ = upper_ - roll
    array_ = np.roll(array, -roll)
    upper_ = upper_ % n
    return array_[lower_: upper_]

Upvotes: 2

Views: 1338

Answers (3)

hpaulj
hpaulj

Reputation: 231540

In [71]: slicer(np.arange(10),-2,2)                                                                    
Out[71]: array([8, 9, 0, 1])

It looks like np.r_ does the kind of 'roll' that you want:

In [72]: np.arange(10)[np.r_[-2:2]]                                                                    
Out[72]: array([8, 9, 0, 1])
In [73]: np.r_[-2:2]                                                                                   
Out[73]: array([-2, -1,  0,  1])

There may be differences between what you expect, and what r_ does. I'll let you study its docs.

Just because you call it slicing, it isn't basic indexing. However done, the result is a copy, not a view. And beware of any kind of extension to multidimensional indexing.

Be careful about seeking an all-case replacement. The use of negative index to mark from-the-end, without wrapping, is so deeply embedded in Python and numpy, that you should always assume that's the default behavior.

In [77]: np.arange(10)[-2:2]                                                                           
Out[77]: array([], dtype=int64)

Treat your wrapped/roll case as an exception, one the requires special handling.

Upvotes: 3

amdex
amdex

Reputation: 781

Given that you know the length, you can just add the length of your array to lower and upper if they are < 0. You can then check if upper is smaller than lower, and concatenate if necessary.

def slicer(a, lower, upper):
    if lower < 0:
        lower += len(a)
    if upper < 0:
        upper += len(a)
    if upper < lower:
        return np.concatenate([a[lower:], a[:upper]])
    return a[lower:upper]

Upvotes: 0

rpoleski
rpoleski

Reputation: 998

def slicer(a, lower, upper):
    if lower < 0:
        return np.concatenate((a[lower:], a[:upper]))
    else:
        return a[lower: upper]

I think it's relatively simple.

Upvotes: 0

Related Questions