Alex Rivera | Logout

Python lists/arrays: disable negative indexing wrap-around in slices

Asked 2012-11-16T04:47:04.343
14

The negative number wraparound (i.e. A[-2] indexing the second-to-last element) is useful in many cases, but when it happens inside a slice it can more of an annoyance than a helpful feature. I've often wished for a way to disable that behavior.

Here is a 2D example

import numpy as np
A = np.random.randint(0, 2, (5, 10))

example

def foo(i, j, r=2):
    """sum of neighbors within r steps of A[i,j]"""
    return A[i-r:i+r+1, j-r:j+r+1].sum()

In the slice above I want any negative number used in the slice to be treated the same as None is, rather than wrapping to the other end of the array.

Because of the wrapping, the otherwise simple implementation above gives incorrect results at boundary conditions and requires some sort of patch like:

def clamp(n):
    return None if n < 0 else n

def ugly_foo(i, j, r=2):
    return A[clamp(i-r):i+r+1, clamp(j-r):j+r+1].sum()

I've tried zero-padding the array or list, but that is still inelegant (requires offsetting the lookup locations indices accordingly) and inefficient (requires copying the array).

Is there a standard trick or elegant solution for slicing without wrapping? Notice that Python and NumPy already handle the case where you specify too large a number nicely - that is, if the index is greater than the shape of the array it behaves the same as if it were None.

Edit
Report

1 Answer

2

I think this isn't ugly enough to justify new classes and wrapping things. Then again it's your code.

def foo(i, j, r=2):
  '''sum of neighbours within r steps of A[i,j]'''
  return A[i-r:abs(i+r+1), j-r:abs(j+r+1)].sum()   # ugly, but works?

(Downvoting is fun, so I've added some more options)

I found out something quite unexpected (for me): The __getslice__(i,j) does not wrap! Instead, negative indices are just ignored, so:

lst[1:3] == lst.__getslice__(1,3)

lst[-3:-1] == 2 next to last items but lst.__getslice__(-3,-1) == []

and finally:

lst[-2:1] == [], but lst.__getslice__(-2,1) == lst[0:1]

Surprising, interesting, and completely useless.

answered 2012-11-16T05:07:10.460

Your Answer