I have indices array
a = np.array([
[0, 0],
[1, 1],
[1, 9]
])
And 2D array
b = np.array([
[0, 1, 2, 3],
[5, 6, 7, 8]
])
I can do this one
b[a[:, 0], a[:, 1]]
But it'll be an exception 'out of bounds', because 9 is out of range. I need a very fast way to make array slice by indices and it will be ideal if I can set a clip value, e.g.:
np.indexing_with_clipping(array=b, indices=a, clipping_value=0)
> array([0, 6, --> 0 = clipped value <--])
clip() function is used to Clip (limit) the values in an array. Given an interval, values outside the interval are clipped to the interval edges. For example, if an interval of [0, 1] is specified, values smaller than 0 become 0, and values larger than 1 become 1.
Indexing can be done in numpy by using an array as an index. In case of slice, a view or shallow copy of the array is returned but in index array a copy of the original array is returned. Numpy arrays can be indexed with other arrays or any other sequence with the exception of tuples.
Two-dimensional (2D) arrays are indexed by two subscripts, one for the row and one for the column. Each element in the 2D array must by the same type, either a primitive type or object type.
Fancy indexing is conceptually simple: it means passing an array of indices to access multiple array elements at once. For example, consider the following array: import numpy as np rand = np. random. RandomState(42) x = rand.
Here's an approach -
def indexing_with_clipping(arr, indices, clipping_value=0):
idx = np.where(indices < arr.shape,indices,clipping_value)
return arr[idx[:, 0], idx[:, 1]]
Sample runs -
In [266]: arr
Out[266]:
array([[0, 1, 2, 3],
[5, 6, 7, 8]])
In [267]: indices
Out[267]:
array([[0, 0],
[1, 1],
[1, 9]])
In [268]: indexing_with_clipping(arr,indices,clipping_value=0)
Out[268]: array([0, 6, 5])
In [269]: indexing_with_clipping(arr,indices,clipping_value=1)
Out[269]: array([0, 6, 6])
In [270]: indexing_with_clipping(arr,indices,clipping_value=2)
Out[270]: array([0, 6, 7])
In [271]: indexing_with_clipping(arr,indices,clipping_value=3)
Out[271]: array([0, 6, 8])
With focus on memory and performance efficiency, here's an approach that modifies the indices within the function -
def indexing_with_clipping_v2(arr, indices, clipping_value=0):
indices[indices >= arr.shape] = clipping_value
return arr[indices[:, 0], indices[:, 1]]
Sample run -
In [307]: arr
Out[307]:
array([[0, 1, 2, 3],
[5, 6, 7, 8]])
In [308]: indices
Out[308]:
array([[0, 0],
[1, 1],
[1, 9]])
In [309]: indexing_with_clipping_v2(arr,indices,clipping_value=2)
Out[309]: array([0, 6, 7])
You can use list comprehension:
b[
[min(x,len(b[0])-1) for x in a[:,0]],
[min(x,len(b[1])-1) for x in a[:,1]]
]
edit I used last array value as your clipping value, but you can replace the min()
function with whatever you want (e.g. trenary operator)
edit2 OK, based on clarification in comments and all python-fu that I could put together, this snipped finally does what you need:
clipping_value = -1
tmp=np.append(b,[[clipping_value],[clipping_value]],axis=1)
tmp[zip(*[((x,y) if (x<b.shape[0] and y<b.shape[1]) else (0,b.shape[1])) for (x,y) in zip(a.transpose()[0],a.transpose()[1])])]
It is the same as above, just creates ndarray tmp
, which is a copy of b
but contains the clipping_value
as its last element and then uses my previous solution to set indices so, that they point to the last element if either of the indices is bigger than dimensions of b
.
I learned that there is reverse to the zip
function and that the numpy arrays accept lists as indices. It was fun. Thanks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With