You can use choose to make the selection:

>>> z_indices.choose(val_arr)
array([[ 9,  1, 20],
       [ 3,  4, 14],
       [24,  7, 17]])

The function choose is incredibly useful, but can be somewhat tricky to make sense of. Essentially, given an array (val_arr) we can make a series of choices (z_indices) from each n-dimensional slice along the first axis.

Also: any fancy indexing operation will create a new array rather than a view of the original data. It is not possible to index val_arr with z_indices without creating a brand new array.

Answer from Alex Riley on Stack Overflow
🌐
Python Like You Mean It
pythonlikeyoumeanit.com › Module3_IntroducingNumpy › AccessingDataAlongMultipleDimensions.html
Accessing Data Along Multiple Dimensions in an Array — Python Like You Mean It
NumPy specifies the row-axis (students) ... each axis (dimension), to uniquely specify an element in this 2D array; the first number specifies an index along axis-0, the second specifies an index along axis-1....
Top answer
1 of 4
16

You can use choose to make the selection:

>>> z_indices.choose(val_arr)
array([[ 9,  1, 20],
       [ 3,  4, 14],
       [24,  7, 17]])

The function choose is incredibly useful, but can be somewhat tricky to make sense of. Essentially, given an array (val_arr) we can make a series of choices (z_indices) from each n-dimensional slice along the first axis.

Also: any fancy indexing operation will create a new array rather than a view of the original data. It is not possible to index val_arr with z_indices without creating a brand new array.

2 of 4
7

With readability, np.choose definitely looks great.

If performance is of essence, you can calculate the linear indices and then use np.take or use a flattened version with .ravel() and extract those specific elements from val_arr. The implementation would look something like this -

def linidx_take(val_arr,z_indices):

    # Get number of columns and rows in values array
     _,nC,nR = val_arr.shape

     # Get linear indices and thus extract elements with np.take
    idx = nC*nR*z_indices + nR*np.arange(nR)[:,None] + np.arange(nC)
    return np.take(val_arr,idx) # Or val_arr.ravel()[idx]

Runtime tests and verify results -

Ogrid based solution from here is made into a generic version for these tests, like so :

In [182]: def ogrid_based(val_arr,z_indices):
     ...:   v_shp = val_arr.shape
     ...:   y,x = np.ogrid[0:v_shp[1], 0:v_shp[2]]
     ...:   return val_arr[z_indices, y, x]
     ...: 

Case #1: Smaller datasize

In [183]: val_arr = np.random.rand(30,30,30)
     ...: z_indices = np.random.randint(0,30,(30,30))
     ...: 

In [184]: np.allclose(z_indices.choose(val_arr),ogrid_based(val_arr,z_indices))
Out[184]: True

In [185]: np.allclose(z_indices.choose(val_arr),linidx_take(val_arr,z_indices))
Out[185]: True

In [187]: %timeit z_indices.choose(val_arr)
1000 loops, best of 3: 230 µs per loop

In [188]: %timeit ogrid_based(val_arr,z_indices)
10000 loops, best of 3: 54.1 µs per loop

In [189]: %timeit linidx_take(val_arr,z_indices)
10000 loops, best of 3: 30.3 µs per loop

Case #2: Bigger datasize

In [191]: val_arr = np.random.rand(300,300,300)
     ...: z_indices = np.random.randint(0,300,(300,300))
     ...: 

In [192]: z_indices.choose(val_arr) # Seems like there is some limitation here with bigger arrays.
Traceback (most recent call last):

  File "<ipython-input-192-10c3bb600361>", line 1, in <module>
    z_indices.choose(val_arr)

ValueError: Need between 2 and (32) array objects (inclusive).


In [194]: np.allclose(linidx_take(val_arr,z_indices),ogrid_based(val_arr,z_indices))
Out[194]: True

In [195]: %timeit ogrid_based(val_arr,z_indices)
100 loops, best of 3: 3.67 ms per loop

In [196]: %timeit linidx_take(val_arr,z_indices)
100 loops, best of 3: 2.04 ms per loop
🌐
GeeksforGeeks
geeksforgeeks.org › python › numpy-index-3d-array-with-index-of-last-axis-stored-in-2d-array
Numpy: Index 3D array with index of last axis stored in 2D array - GeeksforGeeks
July 23, 2025 - Step 2: We use np.expand_dims to add an extra dimension to z_indices, making its shape (3, 3, 1). This is necessary because take_along_axis requires the indices array to have the same number of dimensions as the array being indexed. Step 3: We use np.take_along_axis to select elements from val_arr along the z-axis (axis=0) using the indices from z_indices_expanded. Step 4: We use np.squeeze to remove the extra dimension added by expand_dims, resulting in the final 2D array result_arr. ... import numpy as np # Create a 3D array of shape (3, 3, 3) val_arr = np.arange(27).reshape(3, 3, 3) # Create a 2D array of indices of shape (3, 3) z_indices = np.array([[1, 0, 2], [0, 0, 1], [2, 0, 1]])
🌐
Stack Overflow
stackoverflow.com › questions › 55054603 › index-a-3d-array-with-2d-array-numpy
python - Index a 3D array with 2D array numpy - Stack Overflow
Thus, by traversing the index arrays in tandem, we get all the n-tuples we need to generate the result array, in the same shape as the broadcasted index arrays. ... Our source array is nd_source = np.array(source), which is 2d.
🌐
NumPy
numpy.org › devdocs › user › basics.indexing.html
Indexing on ndarrays — NumPy v2.6.dev0 Manual
The basic slice syntax is i:j:k where i is the starting index, j is the stopping index, and k is the step (\(k\neq0\)). This selects the m elements (in the corresponding dimension) with index values i, i + k, …, i + (m - 1) k where \(m = q + (r\neq0)\) and q and r are the quotient and remainder ...
🌐
Regenerativetoday
regenerativetoday.com › indexing-and-slicing-of-1d-2d-and-3d-arrays-using-numpy
Indexing and Slicing of 1D, 2D and 3D Arrays Using Numpy – Regenerative
So, we can select those as before with x[1:]. All the elements are in first and second rows of both the two-dimensional array. Row index should be represented as 0:2. Column index is 1:4 as the elements are in first, second and third column. Combining all together: ... I hope this helps. Please try with different numbers and slices to learn more. #numpy #numpyarray #python #dataanalysis #datascience #dataanalytics
Find elsewhere
🌐
GeeksforGeeks
geeksforgeeks.org › indexing-multi-dimensional-arrays-in-python-using-numpy
Indexing Multi-dimensional arrays in Python using NumPy | GeeksforGeeks
April 28, 2025 - ... To index a multi-dimensional array you can index with a slicing operation similar to a single dimension array. ... import numpy as np arr_m = np.arange(12).reshape(2, 2, 3) # Indexing print(arr_m[0:3]) print() print(arr_m[1:5:2,::3])
🌐
Stack Overflow
stackoverflow.com › questions › 56152860 › numpy-3d-array-indexing-works-for-2d-how-to-do-for-3d
python - Numpy 3D array Indexing : Works for 2D, how to do for 3D? - Stack Overflow
May 15, 2019 - But now I want to do it for a 3D output array, where key_idx2D is a 2D array with the first dimension representing table_id. Please refer to the figure below: ... key_idx2D = np.array([[1, 2, 1], [2, 2, 2]]) output3D = np.zeros(shape=(key_idx2D.shape[0], len(key_idx), max_out + 1)) key_idx2D = key_idx[np.newaxis, :] # convert to 3D out_idx = out_idx[np.newaxis, :] idx3D = (key_idx2D, out_idx) np.add.at(output3D, idx3D, 1) #IndexError: index 2 is out of bounds for axis 0 with size 2
🌐
Pythoninformer
pythoninformer.com › python-libraries › numpy › index-and-slice
PythonInformer - Indexing and slicing numpy arrays
February 4, 2018 - We can index an element of the array using two indices - i selects the row, and j selects the column: ... Notice the syntax - the i and j values are both inside the square brackets, separated by a comma (the index is actually a tuple (2, 1), but tuple packing is used).
🌐
APXML
apxml.com › courses › essential-numpy-pandas › chapter-3-numpy-array-indexing-slicing › accessing-single-elements
Access NumPy Array Elements
Visual representation of 2D array indexing. The row index comes first, followed by the column index. The highlighted cell arr2d[1, 1] corresponds to the element at row index 1 and column index 1. The concept naturally extends to arrays with more dimensions. For an N-dimensional array, you provide ...
🌐
NumPy
numpy.org › doc › stable › user › basics.indexing.html
Indexing on ndarrays — NumPy v2.5 Manual
The basic slice syntax is i:j:k where i is the starting index, j is the stopping index, and k is the step (\(k\neq0\)). This selects the m elements (in the corresponding dimension) with index values i, i + k, …, i + (m - 1) k where \(m = q + (r\neq0)\) and q and r are the quotient and remainder ...
🌐
Physics Forums
physicsforums.com › more sciences and computing › programming and computer science
Why are new dimensions added to the left in numpy arrays? • Physics Forums
February 26, 2021 - 1D array: single list 2D array: list containing multiple lists as elements 3D array: list containing lists which contain lists as elements Array elements can be address using indices as a[], a[][], a[][][]...