Skip to content

================ by Jawad Haider

Chpt 4 - Visualization with Matplotlib

13 - Three-Dimensional Plotting in Matplotlib



Three-Dimensional Plotting in Matplotlib

Matplotlib was initially designed with only two-dimensional plotting in mind. Around the time of the 1.0 release, some three-dimensional plotting utilities were built on top of Matplotlib’s two-dimensional display, and the result is a convenient (if somewhat limited) set of tools for three-dimensional data visualization. We enable three-dimensional plots by importing the mplot3d toolkit, included with the main Matplotlib installation

from mpl_toolkits import mplot3d
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax=plt.axes(projection='3d')

With this 3D axes enabled, we can now plot a variety of three-dimensional plot types. Three-dimensional plotting is one of the functionalities that benefits immensely from viewing figures interactively rather than statically in the notebook; recall that to use interactive figures, you can use %matplotlib notebook rather than %matplotlib inline when running this code.

%matplotlib notebook

Three-Dimensional Points and Lines

The most basic three-dimensional plot is a line or scatter plot created from sets of (x,y, z) triples. In analogy with the more common two-dimensional plots discussed earlier, we can create these using the ax.plot3D and ax.scatter3D functions. The call signature for these is nearly identical to that of their two-dimensional counterparts, so you can refer to “Simple Line Plots” and “Simple Scatter Plots” for more information on controlling the output.

ax =plt.axes(projection='3d')

# data for  3d line
zline=np.linspace(0,15,1000)
xline=np.sin(zline)
yline=np.cos(zline)
ax.plot3D(xline,yline,zline,'gray')

# data for 3d scatter points
zdata=15*np.random.random(100)
xdata=np.sin(zdata)+0.1*np.random.randn(100)
ydata=np.cos(zdata)+0.1*np.random.randn(100)
ax.scatter3D(xdata,ydata,zdata, c=zdata, cmap='Greens');
<IPython.core.display.Javascript object>

Three-Dimensional Contour Plots

Analogous to the contour plots we explored in “Density and Contour Plots”, mplot3d contains tools to create three-dimensional relief plots using the same inputs. Like two-dimensional ax.contour plots, ax.contour3D requires all the input data to be in the form of two-dimensional regular grids, with the Z data evaluated at each point. Here we’ll show a three-dimensional contour diagram of a three-dimensional sinusoidal function

def f(x,y):
    return np.sin(np.sqrt(x**2+y**2))

x=np.linspace(-6,6,30)
y=np.linspace(-6,6,30)

X,Y=np.meshgrid(x,y)
Z=f(X,Y)
fig=plt.figure()
ax=plt.axes(projection='3d')

ax.contour3D(X,Y,Z, 50,cmap='binary')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
<IPython.core.display.Javascript object>

Sometimes the default viewing angle is not optimal, in which case we can use the view_init method to set the elevation and azimuthal angles. In this example (the result of which is shown below, we’ll use an elevation of 60 degrees (that is, 60 degrees above the x-y plane) and an azimuth of 35 degrees (that is, rotated 35 degrees counter-clockwise about the z-axis):

ax.view_init(60,35)
fig
<IPython.core.display.Javascript object>

Wireframes and Surface Plots

Two other types of three-dimensional plots that work on gridded data are wireframes and surface plots. These take a grid of values and project it onto the specified three- dimensional surface, and can make the resulting three-dimensional forms quite easy to visualize. Here’s an example using a wireframe

fit = plt.figure()
ax=plt.axes(projection='3d')
ax.plot_wireframe(X,Y,Z, color='black')
ax.set_title('WireFrame');
<IPython.core.display.Javascript object>

A surface plot is like a wireframe plot, but each face of the wireframe is a filled poly‐ gon. Adding a colormap to the filled polygons can aid perception of the topology of the surface being visualized

fig= plt.figure()
ax=plt.axes(projection='3d')
ax.plot_surface(X,Y,Z, rstride=1, cstride=1,
               cmap='viridis', edgecolor='none')
ax.set_title('Surface');
<IPython.core.display.Javascript object>

Note that though the grid of values for a surface plot needs to be two-dimensional, it need not be rectilinear. Here is an example of creating a partial polar grid, which when used with the surface3D plot can give us a slice into the function we’re visualiz‐ ing

fig = plt.figure()
r = np.linspace(0, 6, 20)
theta = np.linspace(-0.9 * np.pi, 0.8 * np.pi, 40)
r, theta = np.meshgrid(r, theta)

X = r * np.sin(theta)
Y = r * np.cos(theta)
Z = f(X, Y)

ax = plt.axes(projection='3d')

ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
cmap='viridis', edgecolor='none');
<IPython.core.display.Javascript object>

Surface Triangulations

For some applications, the evenly sampled grids required by the preceding routines are overly restrictive and inconvenient. In these situations, the triangulation-based plots can be very useful. What if rather than an even draw from a Cartesian or a polar grid, we instead have a set of random draws?

theta= 2*np.pi*np.random.random(1000)
r=6*np.random.random(1000)

x=np.ravel(r*np.sin(theta))
y=np.ravel(r*np.cos(theta))
z=f(x,y)
fig=plt.figure()
ax=plt.axes(projection='3d')
ax.scatter(x,y,z,c=z, cmap='viridis',linewidth=0.5)
<IPython.core.display.Javascript object>

<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x7f86f7843820>

This leaves a lot to be desired. The function that will help us in this case is ax.plot_trisurf, which creates a surface by first finding a set of triangles formed between adjacent points

fig=plt.figure()
ax=plt.axes(projection='3d')
ax.plot_trisurf(x,y,z, cmap='viridis',edgecolor='none')
<IPython.core.display.Javascript object>

<mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x7f86f7206460>

The result is certainly not as clean as when it is plotted with a grid, but the flexibility of such a triangulation allows for some really interesting three-dimensional plots. For example, it is actually possible to plot a three-dimensional Möbius strip using this, as we’ll see next.