NumPy arrays iterate over the left-most axis first. Thus if B has shape (2,3,4), then B[0] has shape (3,4) and B[1] has shape (3,4). In this sense, you could think of B as 2 arrays of shape (3,4). You can sort of see the two arrays in the repr of B:

In [233]: B = np.arange(2*3*4).reshape((2,3,4))
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],       <-- first (3,4) array 
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],      <-- second (3,4) array 
        [20, 21, 22, 23]]])

You can also think of B as containing four 2x3 arrays by iterating over the last index first:

for i in range(4):
    print(B[:,:,i])

# [[ 0  4  8]
#  [12 16 20]]
# [[ 1  5  9]
#  [13 17 21]]
# [[ 2  6 10]
#  [14 18 22]]
# [[ 3  7 11]
#  [15 19 23]]

but you could just as easily think of B as three 2x4 arrays:

for i in range(3):
    print(B[:,i,:])

# [[ 0  1  2  3]
#  [12 13 14 15]]
# [[ 4  5  6  7]
#  [16 17 18 19]]
# [[ 8  9 10 11]
#  [20 21 22 23]]

NumPy arrays are completely flexible this way. But as far as the repr of B is concerned, what you see corresponds to two (3x4) arrays since B iterates over the left-most axis first.

for arr in B:
    print(arr)

# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]
# [[12 13 14 15]
#  [16 17 18 19]
#  [20 21 22 23]]
Answer from unutbu on Stack Overflow
Top answer
1 of 3
28

NumPy arrays iterate over the left-most axis first. Thus if B has shape (2,3,4), then B[0] has shape (3,4) and B[1] has shape (3,4). In this sense, you could think of B as 2 arrays of shape (3,4). You can sort of see the two arrays in the repr of B:

In [233]: B = np.arange(2*3*4).reshape((2,3,4))
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],       <-- first (3,4) array 
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],      <-- second (3,4) array 
        [20, 21, 22, 23]]])

You can also think of B as containing four 2x3 arrays by iterating over the last index first:

for i in range(4):
    print(B[:,:,i])

# [[ 0  4  8]
#  [12 16 20]]
# [[ 1  5  9]
#  [13 17 21]]
# [[ 2  6 10]
#  [14 18 22]]
# [[ 3  7 11]
#  [15 19 23]]

but you could just as easily think of B as three 2x4 arrays:

for i in range(3):
    print(B[:,i,:])

# [[ 0  1  2  3]
#  [12 13 14 15]]
# [[ 4  5  6  7]
#  [16 17 18 19]]
# [[ 8  9 10 11]
#  [20 21 22 23]]

NumPy arrays are completely flexible this way. But as far as the repr of B is concerned, what you see corresponds to two (3x4) arrays since B iterates over the left-most axis first.

for arr in B:
    print(arr)

# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]
# [[12 13 14 15]
#  [16 17 18 19]
#  [20 21 22 23]]
2 of 3
8

I hope the below example would clarify the second part of your question where you have asked about getting a 2X3 matrics when you type print(B[:,:,1])

import numpy as np
B = [[[1,2,3,4],
  [5,6,7,8],
  [9,10,11,12]],

 [[13,14,15,16],
  [17,18,19,20],
  [21,22,23,24]]]

B = np.array(B)
print(B)
print()
print(B[:,:,1])

[[[ 1  2  3  4]
  [ 5  6  7  8]
  [ 9 10 11 12]]

 [[13 14 15 16]
  [17 18 19 20]
  [21 22 23 24]]]

[[ 2  6 10]
 [14 18 22]]

Since the dimension of B is 2X3X4, It means you have two matrices of size 3X4 as far as repr of B is concerned

Now in B[:,:,1] we are passing : , : and 1. First : indicates that we are selecting both the 3X4 matrices. The second : indicates that we are selecting all the rows from both the 3X4 matrices. The third parameter 1 indicates that we are selecting only the second column values of all the rows from both the 3X4 matrices. Hence we get

[[ 2  6 10]
 [14 18 22]]
๐ŸŒ
W3Schools
w3schools.com โ€บ python โ€บ numpy โ€บ numpy_array_slicing.asp
NumPy Array Slicing
NumPy Editor NumPy Quiz NumPy Exercises NumPy Syllabus NumPy Study Plan NumPy Certificate ... Slicing in python means taking elements from one given index to another given index. We pass slice instead of index like this: [start:end]. We can ...
๐ŸŒ
Pythoninformer
pythoninformer.com โ€บ python-libraries โ€บ numpy โ€บ index-and-slice
PythonInformer - Indexing and slicing numpy arrays
However, for trailing indices, simply omitting the index counts as a full slice. So for 2D arrays: a2[1:3,:] # is the same as a2[1:3] For 3D arrays: a3[1:,:2,:] # is the same as a3[1:,:2] a3[1:,:,:] # is the same as a3[1:,:] # and is also the same as a3[1:] As we saw earlier, you can use an index to select a particular plane column or row. Here we select row 1, columns 2:4: import numpy as np a2 = np.array([[10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24], [25, 26, 27, 28, 29]]) print(a2[1,2:4]) # [17 18] You can also use a slice of length 1 to do something similar (slice 1:2 instead of index 1): print(a2[1:2,2:4]) # [[17 18]] Notice the subtle difference.
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ python โ€บ python-slicing-multi-dimensional-arrays
Python slicing multi-dimensional arrays - GeeksforGeeks
July 23, 2025 - In this example, we first create a 3-D NumPy array called array_3d. Then, we use negative indexing to slice the last row from each 2-D matrix within the 3-D array.
๐ŸŒ
Reddit
reddit.com โ€บ r/learnpython โ€บ numpy slicing behaving weirdly when slicing three dimensional array. any explanation?
r/learnpython on Reddit: Numpy slicing behaving weirdly when slicing three dimensional array. Any explanation?
April 27, 2022 -

Hi, guys!

I have a three dimensional array x=np.random.randn(10, 5, 5)

When I use: x2 = x[:, [1, 2], [1, 2]] I should get an array of size [10, 2, 2] but I don't. Instead I get an array of shape [10, 2].

To get what I want, I have to use two lines of code: x2 = x[:, [1, 2], :]; x2=x2[:, :, [1, 2]].

I don't understand why is numpy behaving in this manner. Any explanation is much appreciated

Thank you!

๐ŸŒ
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
We can select these two with x[1:]. As both of the rows are the first row of its corresponding two-dimensional array, row index is zero. ... Slice through both columns and rows and print part of first two rows of the last two two-dimensional arrays
๐ŸŒ
NumPy
numpy.org โ€บ devdocs โ€บ user โ€บ basics.indexing.html
Indexing on ndarrays โ€” NumPy v2.5.dev0 Manual
The standard rules of sequence slicing apply to basic slicing on a per-dimension basis (including using a step index). Some useful concepts to remember include: 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\)).
Find elsewhere
๐ŸŒ
Spark By {Examples}
sparkbyexamples.com โ€บ home โ€บ python โ€บ numpy array slicing
NumPy Array Slicing - Spark By {Examples}
March 27, 2024 - NumPy array slicing is a technique in the NumPy library that allows you to extract a portion of an array to create a new array. It provides a convenient way to access and manipulate specific elements, rows, or columns within an array.
๐ŸŒ
Python Tutorial
pythontutorial.net โ€บ home โ€บ python numpy โ€บ numpy array slicing
Numpy Array Slicing
August 16, 2022 - To slice a multidimensional array, you apply the square brackets [] and the : notation to each dimension (or axis). The slice returns a reduced array where each element matches the selection rules.
๐ŸŒ
NumPy
numpy.org โ€บ doc โ€บ stable โ€บ user โ€บ basics.indexing.html
Indexing on ndarrays โ€” NumPy v2.4 Manual
The standard rules of sequence slicing apply to basic slicing on a per-dimension basis (including using a step index). Some useful concepts to remember include: 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\)).
๐ŸŒ
TutorialsPoint
tutorialspoint.com โ€บ home โ€บ numpy โ€บ numpy slicing
NumPy Slicing
March 5, 2015 - Let us create a 3D array using ... representing a (2*3*4) matrix and use slice object for slicing to get a subarray which selects the first layer (depth), all rows, and the first 2 columns โˆ’ ยท import numpy as np arr_3d = ...
Top answer
1 of 1
5

This computation is not slow because of Numpy, but because of your hardware, and actually on most modern hardware. The main reason it is slow is because of random RAM accesses resulting in a latency-bound computation.

The input array is big and thus cannot be stored in CPU cache but in RAM. Modern RAM can have a quite high throughput but each fetch require a pretty big latency (about 80ns per random fetch on recent x86 processors). While the throughput of new RAM devices tends to significantly improve over time, the it is barely the case for latency. Fetching one (8-bytes) double-precision floating-point number at a time sequentially would result in a throughput of size_of_double/RAM_latency = 8/80e-9 โ‰ˆ 95 MiB/s. This is a fraction of what modern mainstream RAM devices can do (dozens of GiB/s)

To solve this problem, modern processors can fetch several memory block at a time and try to predict RAM accesses and load data ahead of time using prefetching units and speculation. This works well for predictible access patterns (especially contiguous loads) but not at all with a random access pattern like in your code. Still modern processors succeed to fetch multiple memory blocks in parallel with on a sequential code, but not enough so this kind of code can be fast enough (the throughput of your code is about 400 MiB/s). Not to mention mainstream x86 processors load systematically cache-lines of 64-bytes from RAM devices while you only need 8-bytes.

One solution is to parallelize this operation. However, this is not very efficient because of cache-lines (you will barely get more than 10% of the maximal throughput).

An alternative solution is to transpose your input data so that so that the fetched memory blocks can be more contiguous. Here is an example:

transposedLookup = np.array(lookup.T)
%timeit transposedLookup[m[:,0], m[:,1],:].T

Note that the first transposition will be rather slow (mainly because it is not yet optimized by Numpy, but also because of the access pattern) and requires twice the amount of RAM. You can use this solution to speed up the transposition. It is also possible to transpose the input matrix in-place if it is cubic. It would be even better if you could generate the array directly in its transposed form.

Also note that the second transposition is fast because it is done lazily, but even an eagerly transposition is still several times faster than the original code.

Here are the timings on my machine:

Original code: 14.1 ms
Eager Numpy transposition: 2.6 ms
Lazy Numpy transposition: 0.6 ms

EDIT: note that the same thing apply for m.

Top answer
1 of 5
1
arr = np.array([[[1,2,3], [4,5,6]], [[7,8,9],[10,11,12]]])

# array([[[ 1,  2,  3],
#         [ 4,  5,  6]],

#        [[ 7,  8,  9],
#         [10, 11, 12]]])

arr.shape # ------> (2,2,3)
# Think of them as axis
# lets create the first 2 axis of (`2`, ...)

#         |(1)
#         |
#         |
#         |         
#         |---------(0)


# now lets create second 2 axis of (2, `2`, ..)

#            (1,1)
#         |
#         |---(1,0)
#         |
#         |
#         |
#         |         |(0,1)
#         |---------|---(0,0)

# now lets create the last 3 axis of (2, 2, `3`)

#           /``(1,1,0) = 10
#          |-- (1,1,1) = 11
#         | \__(1,1,2) = 12
#         |
#         |  /``(1,0,0) = 7
#         |--|--(1,0,1) = 8
#         |  \__(1,0,2) = 9
#         |
#         |
#         |         /``(0,1,0) = 4
#         |         |--(0,1,1) = 5
#         |         \__(0,1,2) = 6
#         |         |
#         |         |
#         |---------|---/``(0,0,0) = 1
#                       |--(0,0,1) = 2
#                       \__(0,0,2) = 3

# now suppose you ask
arr[0, :, :] # give me the first axis alon with all it's component

#         |
#         |         /``(0,1,0) = 4
#         |         |--(0,1,1) = 5
#         |         \__(0,1,2) = 6
#         |         |
#         |         |
#         |---------|---/``(0,0,0) = 1
#                       |--(0,0,1) = 2
#                       \__(0,0,2) = 3

# So it will print 

# array([[1, 2, 3],
#        [4, 5, 6]])

arr[:, 0, :] # you ask take all the first axis 1ut give me only the first axis of the first axis and all its components

#           
#         
#         
#         
#         |  /``(1,0,0) = 7
#         |--|--(1,0,1) = 8
#         |  \__(1,0,2) = 9
#         |
#         |
#         |         
#         |         
#         |         
#         |         
#         |         
#         |---------|---/``(0,0,0) = 1
#                       |--(0,0,1) = 2
#                       \__(0,0,2) = 3

# so you get the output

# array([[1, 2, 3],
#        [7, 8, 9]])

# like wise you ask
print(arr[0:2, : , 2])
# so you are saying give (0,1) first axis, all of its children and only 3rd (index starts at 0 so 2 means 3) children
# 0:2 means 0 to 2 `excluding` 2; 0:5 means 0,1,2,3,4

#           
#          |
#         | \__(1,1,2) = 12
#         |
#         |  
#         |--
#         |  \__(1,0,2) = 9
#         |
#         |
#         |        
#         |         
#         |         \__(0,1,2) = 6
#         |         |
#         |         |
#         |---------|---/
#                       |
#                       \__(0,0,2) = 3

# so you get

# array([[ 3,  6],
#        [ 9, 12]])
2 of 5
1

Going In

Given the array:

    arr = np.array([[[1,2,3], [4,5,6]], [[7,8,9],[10,11,12]]])

The shape of arr, given by arr.shape is (2,2,3).

That basically means that if we start from outside (and ignore the first square bracket), we have 2 arrays in that scope. If we enter to one of those we can count 2 arrays and if we enter either we find 3 elements. I think having this view is quite helpful in understanding the following.

Specifying arr[1,1,2] selects the second array(index 1) in the outermost scope, and then selects the second array in the following scope and then selects the third element. The output is a single number:

    12

specifying arr[:,1,2] first simultaneously selects all arrays at the outermost scope and then for each of these selects the second array (index 1). When we then enter into the next scope, we pick out the 3rd elements. This outputs two numbers:

    array([ 6, 12])

specifying arr[:, : , 2] outputs 4 numbers since 1. at the first scope we selected all arrays (2) 2. at the next scope we selected all array (2 for each in the first)

    array([[ 3,  6],
           [ 9, 12]])

Coming out

Instinctually, the reason why they appear as 2x2 array can be viewed as receding from the lowermost scope. Elements are getting enclosed in square brackets because that share a scope. 3 and 6 will be in one array while 9 and 12 will be in another array.

๐ŸŒ
Python Guides
pythonguides.com โ€บ python-numpy-3d-array
3D Arrays In Python Using NumPy
May 16, 2025 - import numpy as np # Create a sample 3D array array_3d = np.arange(24).reshape(2, 3, 4) print("Original 3D array:") print(array_3d) # Get a specific 2D array (the first one) first_matrix = array_3d[0] print("\nFirst 2D matrix:") print(first_matrix) # Get a specific row from a specific 2D array second_matrix_first_row = array_3d[1, 0] print("\nFirst row of second matrix:") print(second_matrix_first_row) # Get a specific element specific_element = array_3d[1, 2, 3] print(f"\nElement at [1,2,3]: {specific_element}") # Slice across all dimensions subset = array_3d[0:2, 1:3, 1:3] print("\nSubset sl
๐ŸŒ
Pluralsight
pluralsight.com โ€บ tech insights & how-to guides โ€บ tech guides & tutorials
Working with Numpy Arrays: Indexing & Slicing | Pluralsight
For example, let me define a one-dimensional array ... Index โ€˜3โ€™ represents the starting element of the slice and it's inclusive. Index โ€˜6โ€™ represents the stopping element of the slice and itโ€™s exclusive. That's the reason why we did not get the value โ€˜6โ€™ in the output. If you do not specify the starting and the stopping index you will get all the values. ... That's because if the indices are missing, by default, Numpy inserts the starting and stopping indices that select the entire array.
๐ŸŒ
VTK
discourse.vtk.org โ€บ support
Creating 3D objects and slice them to obtain 2D numpy array - Support - VTK
October 25, 2024 - Dear all, this might be a stupid question, but I am completely new in the topic and I need some help in understanding the best approach. I am creating an anatomical model, and the idea is that when Iโ€™m done with it, I need to be able to slice it with planes and convert the slice to a numpy ...
๐ŸŒ
Medium
medium.com โ€บ @whyamit101 โ€บ understanding-numpy-slicing-with-examples-57351d21387c
Understanding NumPy Slicing with Examples | by why amit | Medium
February 9, 2025 - โ€œYou know that feeling when youโ€™re trying to get just one piece of cake from the whole batch but end up taking an extra slice because you canโ€™t resist? NumPy slicing is kind of like that โ€” selecting exactly the parts you want from an array without touching the rest.โ€