Convert array indexing code from 1D to 3D without using loops in python

Question:

I have a 3D matrix that I want to index. The indexes are selected in a GUI so they may be out of range. I want to set values in the matrix if an index is out of range. Right now I have a code that does it with loops that is something like this:

list=[]
for i in range(): 
    if X,Y,Z out of range:
        a=1000
        list.append(a)
    else:  
        a=array_3d[X,Y,Z]
        list.append(a)

Obviously, this is a slow method when the list becomes a high length. I have a code that indexes a one dimensional list as I want.

”’

import numpy as np

class Set_Value(list):
    def _get_item_with_default(self, index, default_value):
        return super(Set_Value, self).__getitem__(index) if index >= 0 and index < len(self) else default_value

    def __getitem__(self, index):
        if isinstance(index, int):
            return self._get_item_with_default(index, 1000)
        elif isinstance(index, list):
            return [self._get_item_with_default(elem, 1000) for elem in index]

A=np.array([100,200,300,400])
S=Set_Value(A)
X=[1,1,1,1,1,1,1,1,1,1]
Y=[1,1,1,-5,-5,-5,1,1,1,1]
print(S[X])
print(S[Y])

”’

OUTPUT: 
[200, 200, 200, 200, 200, 200, 200, 200, 200, 200]
[200, 200, 200, 1000, 1000, 1000, 200, 200, 200, 200]

I’m struggling to convert this to 3D, ie

”’

import numpy as np
import random

Class TestC():
    #stuff to solve problem

array3d=np.random.randint(0,1000,size=(50,50,50))
set_3d=TestC(array3d)

X=random.sample(range(-100, 100), 100)
Y=random.sample(range(-100, 100), 100)
Z=random.sample(range(-100, 100), 100)
print(set_3d[X,Y,Z])

”’

OUTPUT: 

[value pulled from array3d, value pulled from array3d, set value to 1000 if out of range, set value to 1000 if out of range, ...]

At this point, I am not even sure if it will be faster, but I am just curious if it can be done, and I cannot get it to work.

Asked By: BH10001

||

Answers:

As hinted by hpaulj, np.clip and np.where are useful here; no need to create a custom class.

# Convert all the indices into NumPy arrays
x, y, z = np.array(X), np.array(Y), np.array(Z)

# Keep track of out-of-range indices
is_x_oor = (x < 0) | (x >= array3d.shape[0])
is_y_oor = (y < 0) | (y >= array3d.shape[1])
is_z_oor = (z < 0) | (z >= array3d.shape[2])

# Ensure the indices stay within range
x = x.clip(0, array3d.shape[0] - 1)
y = y.clip(0, array3d.shape[1] - 1)
z = z.clip(0, array3d.shape[2] - 1)

# Perform indexing but take the default value if any index is out of range
res = np.where(is_x_oor | is_y_oor | is_z_oor, 1000, array3d[x, y, z])

You can then convert the result to list with res.tolist().

Answered By: kmkurn
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.