Falko's suggestion to use contourf works with a bit of finagling. It's a bit limited since at least my version of contourf has a few bugs where it sometimes renders one of the planes in front of other planes it should be behind, but for now only plotting either the three front or three back sides of the cube will do:

import numpy as np
import math
import matplotlib.pyplot as plot
import mpl_toolkits.mplot3d.axes3d as axes3d

def cube_marginals(cube, normalize=False):
    c_fcn = np.mean if normalize else np.sum
    xy = c_fcn(cube, axis=0)
    xz = c_fcn(cube, axis=1)
    yz = c_fcn(cube, axis=2)
    return(xy,xz,yz)

def plotcube(cube,x=None,y=None,z=None,normalize=False,plot_front=False):
    """Use contourf to plot cube marginals"""
    (Z,Y,X) = cube.shape
    (xy,xz,yz) = cube_marginals(cube,normalize=normalize)
    if x == None: x = np.arange(X)
    if y == None: y = np.arange(Y)
    if z == None: z = np.arange(Z)

    fig = plot.figure()
    ax = fig.gca(projection='3d')

    # draw edge marginal surfaces
    offsets = (Z-1,0,X-1) if plot_front else (0, Y-1, 0)
    cset = ax.contourf(x[None,:].repeat(Y,axis=0), y[:,None].repeat(X,axis=1), xy, zdir='z', offset=offsets[0], cmap=plot.cm.coolwarm, alpha=0.75)
    cset = ax.contourf(x[None,:].repeat(Z,axis=0), xz, z[:,None].repeat(X,axis=1), zdir='y', offset=offsets[1], cmap=plot.cm.coolwarm, alpha=0.75)
    cset = ax.contourf(yz, y[None,:].repeat(Z,axis=0), z[:,None].repeat(Y,axis=1), zdir='x', offset=offsets[2], cmap=plot.cm.coolwarm, alpha=0.75)

    # draw wire cube to aid visualization
    ax.plot([0,X-1,X-1,0,0],[0,0,Y-1,Y-1,0],[0,0,0,0,0],'k-')
    ax.plot([0,X-1,X-1,0,0],[0,0,Y-1,Y-1,0],[Z-1,Z-1,Z-1,Z-1,Z-1],'k-')
    ax.plot([0,0],[0,0],[0,Z-1],'k-')
    ax.plot([X-1,X-1],[0,0],[0,Z-1],'k-')
    ax.plot([X-1,X-1],[Y-1,Y-1],[0,Z-1],'k-')
    ax.plot([0,0],[Y-1,Y-1],[0,Z-1],'k-')

    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    plot.show()

plot_front=True plot_front=False Other data (not shown)

Answer from Andrew Schwartz on Stack Overflow
Top answer
1 of 2
8

Falko's suggestion to use contourf works with a bit of finagling. It's a bit limited since at least my version of contourf has a few bugs where it sometimes renders one of the planes in front of other planes it should be behind, but for now only plotting either the three front or three back sides of the cube will do:

import numpy as np
import math
import matplotlib.pyplot as plot
import mpl_toolkits.mplot3d.axes3d as axes3d

def cube_marginals(cube, normalize=False):
    c_fcn = np.mean if normalize else np.sum
    xy = c_fcn(cube, axis=0)
    xz = c_fcn(cube, axis=1)
    yz = c_fcn(cube, axis=2)
    return(xy,xz,yz)

def plotcube(cube,x=None,y=None,z=None,normalize=False,plot_front=False):
    """Use contourf to plot cube marginals"""
    (Z,Y,X) = cube.shape
    (xy,xz,yz) = cube_marginals(cube,normalize=normalize)
    if x == None: x = np.arange(X)
    if y == None: y = np.arange(Y)
    if z == None: z = np.arange(Z)

    fig = plot.figure()
    ax = fig.gca(projection='3d')

    # draw edge marginal surfaces
    offsets = (Z-1,0,X-1) if plot_front else (0, Y-1, 0)
    cset = ax.contourf(x[None,:].repeat(Y,axis=0), y[:,None].repeat(X,axis=1), xy, zdir='z', offset=offsets[0], cmap=plot.cm.coolwarm, alpha=0.75)
    cset = ax.contourf(x[None,:].repeat(Z,axis=0), xz, z[:,None].repeat(X,axis=1), zdir='y', offset=offsets[1], cmap=plot.cm.coolwarm, alpha=0.75)
    cset = ax.contourf(yz, y[None,:].repeat(Z,axis=0), z[:,None].repeat(Y,axis=1), zdir='x', offset=offsets[2], cmap=plot.cm.coolwarm, alpha=0.75)

    # draw wire cube to aid visualization
    ax.plot([0,X-1,X-1,0,0],[0,0,Y-1,Y-1,0],[0,0,0,0,0],'k-')
    ax.plot([0,X-1,X-1,0,0],[0,0,Y-1,Y-1,0],[Z-1,Z-1,Z-1,Z-1,Z-1],'k-')
    ax.plot([0,0],[0,0],[0,Z-1],'k-')
    ax.plot([X-1,X-1],[0,0],[0,Z-1],'k-')
    ax.plot([X-1,X-1],[Y-1,Y-1],[0,Z-1],'k-')
    ax.plot([0,0],[Y-1,Y-1],[0,Z-1],'k-')

    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    plot.show()

plot_front=True plot_front=False Other data (not shown)

2 of 2
2

Take a look at MayaVI. The contour3d() function may be what you want.

Here's an answer I gave to a similar question with an example of the code and resulting plot https://stackoverflow.com/a/24784471/3419537

Discussions

python - Very Basic Numpy array dimension visualization - Stack Overflow
I'm a beginner to numpy with no experience in matrices. I understand basic 1d and 2d arrays but I'm having trouble visualizing a 3d numpy array like the one below. How do the following python lists... More on stackoverflow.com
🌐 stackoverflow.com
Best way to visualize a 3d numpy array?
You can use voxels https://matplotlib.org/stable/gallery/mplot3d/voxels_rgb.html However these are usually opaque so you'll have to do some filtering operation to isolate only those values you're interested in. You mention temperature; perhaps you're interested in a hot region around some feature so you could filter out voxels with lower temperature. If you can pull in another library like plotly then you can use true volume rendering https://plotly.com/python/3d-volume-plots/ More on reddit.com
🌐 r/learnpython
6
2
February 24, 2023
matplotlib - What is the most efficient way to plot 3d array in Python? - Stack Overflow
What is the most efficient way to plot 3d array in Python? For example: volume = np.random.rand(512, 512, 512) where array items represent grayscale color of each pixel. The following code works ... More on stackoverflow.com
🌐 stackoverflow.com
python - How to plot a matrix as a 3D imshow plot in matplotlib? - Stack Overflow
I am trying to plot a 4x4 array as a 3D plot in matplotlib, but I'm facing issues with the plot_surface function. The resulting graph only displays a surface with a 3x3 color grid, instead of a 4x4 color grid. I suspect that plot_surface may not be the appropriate approach for visualizing the complete matrix... More on stackoverflow.com
🌐 stackoverflow.com
🌐
GitHub
github.com › KD5VMF › CHAT-GPT4-3D_Matrix
GitHub - KD5VMF/CHAT-GPT4-3D_Matrix: 3D graphical representation of a matrix multiplication algorithm.
3D graphical representation of a matrix multiplication algorithm. This Python program provides a command-line interface for users to generate two random matrices, perform matrix multiplication, and visualize the resulting matrix in a 3D plot.
Author: KD5VMF
Top answer
1 of 1
33

The anatomy of an ndarray in NumPy looks like this red cube below: (source: Physics Dept, Cornell Uni)


Once you leave the 2D space and enter 3D or higher dimensional spaces, the concept of rows and columns doesn't make much sense anymore. But still you can intuitively understand 3D arrays. For instance, considering your example:

In [41]: b
Out[41]: 
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

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

In [42]: b.shape
Out[42]: (2, 2, 3)

Here the shape of b is (2, 2, 3). You can think about it like, we've two (2x3) matrices stacked to form a 3D array. To access the first matrix you index into the array b like b[0] and to access the second matrix, you index into the array b like b[1].

# gives you the 2D array (i.e. matrix) at position `0`
In [43]: b[0]
Out[43]: 
array([[1, 2, 3],
       [4, 5, 6]])


# gives you the 2D array (i.e. matrix) at position 1
In [44]: b[1]
Out[44]: 
array([[ 7,  8,  9],
       [10, 11, 12]])

However, if you enter 4D space or higher, it will be very hard to make any sense out of the arrays itself since we humans have hard time visualizing 4D and more dimensions. So, one would rather just consider the ndarray.shape attribute and work with it.


More information about how we build higher dimensional arrays using (nested) lists:

For 1D arrays, the array constructor needs a sequence (tuple, list, etc) but conventionally list is used.

In [51]: oneD = np.array([1, 2, 3,])    
In [52]: oneD.shape
Out[52]: (3,)

For 2D arrays, it's list of lists but can also be tuple of lists or tuple of tuples etc:

In [53]: twoD = np.array([[1, 2, 3], [4, 5, 6]])
In [54]: twoD.shape
Out[54]: (2, 3)

For 3D arrays, it's list of lists of lists:

In [55]: threeD = np.array([[[1, 2, 3], [2, 3, 4]], [[5, 6, 7], [6, 7, 8]]])

In [56]: threeD.shape
Out[56]: (2, 2, 3)

P.S. Internally, the ndarray is stored in a memory block as shown in the below picture. (source: Enthought)

🌐
Python Data Science Handbook
jakevdp.github.io › PythonDataScienceHandbook › 04.12-three-dimensional-plotting.html
Three-Dimensional Plotting in Matplotlib | Python Data Science Handbook
These take a grid of values and ... to visualize. Here's an example of using a wireframe: ... fig = plt.figure() ax = plt.axes(projection='3d') ax.plot_wireframe(X, Y, Z, color='black') ax.set_title('wireframe');...
Top answer
1 of 3
6

For better performance, avoid calling ax.scatter multiple times, if possible. Instead, pack all the x,y,z coordinates and colors into 1D arrays (or lists), then call ax.scatter once:

ax.scatter(x, y, z, c=volume.ravel())

The problem (in terms of both CPU time and memory) grows as size**3, where size is the side length of the cube.

Moreover, ax.scatter will try to render all size**3 points without regard to the fact that most of those points are obscured by those on the outer shell.

It would help to reduce the number of points in volume -- perhaps by summarizing or resampling/interpolating it in some way -- before rendering it.

We can also reduce the CPU and memory required from O(size**3) to O(size**2) by only plotting the outer shell:

import functools
import itertools as IT
import numpy as np
import scipy.ndimage as ndimage
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def cartesian_product_broadcasted(*arrays):
    """
    http://stackoverflow.com/a/11146645/190597 (senderle)
    """
    broadcastable = np.ix_(*arrays)
    broadcasted = np.broadcast_arrays(*broadcastable)
    dtype = np.result_type(*arrays)
    rows, cols = functools.reduce(np.multiply, broadcasted[0].shape), len(broadcasted)
    out = np.empty(rows * cols, dtype=dtype)
    start, end = 0, rows
    for a in broadcasted:
        out[start:end] = a.reshape(-1)
        start, end = end, end + rows
    return out.reshape(cols, rows).T

# @profile  # used with `python -m memory_profiler script.py` to measure memory usage
def main():
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1, projection='3d')

    size = 512
    volume = np.random.rand(size, size, size)
    x, y, z = cartesian_product_broadcasted(*[np.arange(size, dtype='int16')]*3).T
    mask = ((x == 0) | (x == size-1) 
            | (y == 0) | (y == size-1) 
            | (z == 0) | (z == size-1))
    x = x[mask]
    y = y[mask]
    z = z[mask]
    volume = volume.ravel()[mask]

    ax.scatter(x, y, z, c=volume, cmap=plt.get_cmap('Greys'))
    plt.show()

if __name__ == '__main__':
    main()

But note that even when plotting only the outer shell, to achieve a plot with size=512 we still need around 1.3 GiB of memory. Also beware that even if you have enough total memory but, due to a lack of RAM, the program uses swap space, then the overall speed of the program will slow down dramatically. If you find yourself in this situation, then the only solution is to find a smarter way to render an acceptable image using fewer points, or to buy more RAM.

2 of 3
5

First, a dense grid of 512x512x512 points is way too much data to plot, not from a technical perspective but from being able to see anything useful from it when observing the plot. You probably need to extract some isosurfaces, look at slices, etc. If most of the points are invisible, then it's probably okay, but then you should ask ax.scatter to only show the nonzero points to make it faster.

That said, here's how you can do it much more quickly. The tricks are to eliminate all Python loops, including ones that would be hidden in libraries like itertools.

import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt

# Make this bigger to generate a dense grid.
N = 8

# Create some random data.
volume = np.random.rand(N, N, N)

# Create the x, y, and z coordinate arrays.  We use 
# numpy's broadcasting to do all the hard work for us.
# We could shorten this even more by using np.meshgrid.
x = np.arange(volume.shape[0])[:, None, None]
y = np.arange(volume.shape[1])[None, :, None]
z = np.arange(volume.shape[2])[None, None, :]
x, y, z = np.broadcast_arrays(x, y, z)

# Turn the volumetric data into an RGB array that's
# just grayscale.  There might be better ways to make
# ax.scatter happy.
c = np.tile(volume.ravel()[:, None], [1, 3])

# Do the plotting in a single call.
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter(x.ravel(),
           y.ravel(),
           z.ravel(),
           c=c)
Find elsewhere
🌐
GeeksforGeeks
geeksforgeeks.org › python › three-dimensional-plotting-in-python-using-matplotlib
Three-dimensional Plotting in Python using Matplotlib - GeeksforGeeks
Visualizing data involving three ... plots cannot reveal. Python’s Matplotlib library, through its mpl_toolkits.mplot3d toolkit, provides powerful support for 3D visualizations....
Published: July 15, 2025
🌐
Kaggle
kaggle.com › questions-and-answers › 449166
How to show the structure of 3d numpy array in python | Kaggle
Hello everyone. I was wondered to know how we can plot 3d numpy arrays in python. Like this I mean the way we can see this image, we can plot it. Thanks
🌐
Jay Alammar
jalammar.github.io › visual-numpy
A Visual Intro to NumPy and Data Representation
June 26, 2019 - You can pass -1 for a dimension and NumPy can infer the correct dimension based on your matrix: NumPy can do everything we’ve mentioned in any number of dimensions. Its central data structure is called ndarray (N-Dimensional Array) for a reason. In a lot of ways, dealing with a new dimension is just adding a comma to the parameters of a NumPy function: Note: Keep in mind that when you print a 3-dimensional NumPy array, the text output visualizes the array differently than shown here.
🌐
Medium
medium.com › @NavSpeak › making-sense-of-numpy-axes-how-to-visualize-arrays-in-3d-13474aeaeca4
Making Sense of NumPy Axes: How to Visualize Arrays in 3D | by Navspeak | Medium
November 13, 2025 - We have three layers. At layer i, the matrix has values · [['ai','bi','ci','di'], ['ei','fi','gi','hi'], #LAYER i ['ii','ji','ki','lj']] We can visualize it in 3D as 3 layers forming a cube
Top answer
1 of 1
2

This answer creates a 3D barplot as an alternative method for representing your data. This will allow you to visualize the surface with each column representing a single z-value. It is also possible to view a flattened heat map of the array by rotating the figure to display the 'bottom'.

NOTE: I used a different, larger sample set in order to create the examples. This is just to show that the method works for more complex inputs.

Example Output

This is a barplot of a larger sample set. You can clearly see the data being represented even though the 'surface' of the data is not flat.

This is a bottom-up view of the figure. It represents a heatmap of your input data.

Code

from matplotlib import cbook
from matplotlib import cm
import matplotlib.colors as colors
import matplotlib.pyplot as plt
import numpy as np

# Load and format data
dem = cbook.get_sample_data('jacksboro_fault_dem.npz', np_load=True)
z = dem['elevation']
nrows, ncols = z.shape
x = np.linspace(dem['xmin'], dem['xmax'], ncols)
y = np.linspace(dem['ymin'], dem['ymax'], nrows)
x, y = np.meshgrid(x, y)

region = np.s_[5:50, 5:50]
x, y, z = x[region].ravel(), y[region].ravel(), z[region].ravel()

# parameters for bar3d(...) func
bottom = np.full_like(z, np.min(z))
width = (np.max(x)-np.min(x))/np.sqrt(np.shape(x)[0])
depth = (np.max(y)-np.min(y))/np.sqrt(np.shape(y)[0])

# creating color_values for the figure
offset = z + np.abs(z.min())
fracs = offset.astype(float)/offset.max()
norm = colors.Normalize(fracs.min(), fracs.max())
color_values = cm.jet(norm(fracs.tolist()))

# Set up and display the plot
fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
ax.bar3d(x,y,bottom,width,depth,z, color=color_values, shade=True)

## Used to view the figure from the bottom
# ax.view_init(270, 0)

plt.show()

Does this help?

Documentation

3D Bar Charts from Matplotlib

Another example using the sample data from Matplotlib

🌐
LinkedIn
linkedin.com › pulse › python-3d-visualization-hackable-step-by-step-jupyter-duc-haba
Python 3D Visualization -- A Hackable Step-by- ...
June 16, 2021 - The above code-cell takes ten code-lines for converting the parametric "horse saddle" equation to our 3D-data set. You can do it in fewer code-lines using Python and numpy advanced matrix multiplication functions and syntax.
🌐
PyVista
pyvista.org
PyVista | 3D plotting & analysis made easy
PyVista is a Python library for 3D visualization and mesh analysis that works naturally with NumPy, pandas, xarray, and the rest of the scientific Python ecosystem.
Top answer
1 of 2
33

If you have a dset like that, and you want to just get the 1 values, you could use nonzero, which "returns a tuple of arrays, one for each dimension of a, containing the indices of the non-zero elements in that dimension.".

For example, we can make a simple 3d array:

>>> import numpy
>>> numpy.random.seed(29)
>>> d = numpy.random.randint(0, 2, size=(3,3,3))
>>> d
array([[[1, 1, 0],
        [1, 0, 0],
        [0, 1, 1]],

       [[0, 1, 1],
        [1, 0, 0],
        [0, 1, 1]],

       [[1, 1, 0],
        [0, 1, 0],
        [0, 0, 1]]])

and find where the nonzero elements are located:

>>> d.nonzero()
(array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2]), array([0, 0, 1, 2, 2, 0, 0, 1, 2, 2, 0, 0, 1, 2]), array([0, 1, 0, 1, 2, 1, 2, 0, 1, 2, 0, 1, 1, 2]))
>>> z,x,y = d.nonzero()

If we wanted a more complicated cut, we could have done something like (d > 3.4).nonzero() or something, as True has an integer value of 1 and counts as nonzero.

Finally, we plot:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x, y, -z, zdir='z', c= 'red')
plt.savefig("demo.png")

giving

2 of 2
1

If you wanted to avoid using the nonzero option (for example, if you had a 3D numpy array whose values were supposed to be the color values of the data points), you could do what you do, but save some lines of code by using ndenumerate.

Your example might become:

for index, x in np.ndenumerate(dset):
    if x == 1:
        ax.scatter(*index, c = 'red')

I guess the point is just that you dont need to have nested for loops to iterate through multidimensional numpy arrays.

🌐
Arrayviz
arrayviz.com
Interactive 3D Array Visualizer | JSON, CSV & NumPy Matrix Viewer
Paste your data and see it rendered in 3D immediately. This tool requires no setup or installation to visualize complex matrix structures.
🌐
Matplotlib
matplotlib.org › stable › gallery › mplot3d › index.html
3D plotting — Matplotlib 3.11.2 documentation
Skip to main content · Back to top · Plot types · User guide · Tutorials · Examples · Reference · Contribute · Releases · Choose version
🌐
AskPython
askpython.com › python-modules › matplotlib › 3-dimensional-plots-in-python
3-Dimensional Plots in Python Using Matplotlib - AskPython
December 14, 2020 - from mpl_toolkits import mplot3d import numpy as np import matplotlib.pyplot as plt #create 3d axes fig = plt.figure() ax = plt.axes(projection='3d') #set title ax.set_title('Learning about 3D plots') plt.show()
🌐
Berkeley
pythonnumericalmethods.studentorg.berkeley.edu › notebooks › chapter12.02-3D-Plotting.html
3D Plotting — Python Numerical Methods
A third array, Z, can then be created ... the np.meshgrid function in Python. The meshgrid function has the inputs x and y are lists containing the independent data set. The output variables X and Y are as described earlier. TRY IT! Create a mesh for x = [1, 2, 3, 4] and y = [3, 4, 5] using the meshgrid function. x = [1, 2, 3, 4] y = [3, 4, 5] X, Y = np.meshgrid(x, y) print(X) ... We could plot 3D surfaces in Python too, the function to plot the 3D surfaces is plot_surface(X,Y,Z), where X and ...