Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Plot contours of distribution on all three axes in 3D plot

I have a cloud of points in three-dimensional space, and have estimated some distribution over those points (also in 3D space; using kernel density estimation although that's irrelevant for this question). I would like to plot the projection of that distribution as a contour plot onto all three axes (x, y, and z). It is straightforward to do this for the z-axis (i.e. project onto plane with same z-coordinate everywhere):

import numpy as np
import scipy as sp
import scipy.stats
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d

# generate some points of a 3D Gaussian
points = np.random.normal(size=(3, 50))

# do kernel density estimation to get smooth estimate of distribution
# make grid of points
x, y, z = np.mgrid[-4:4:100j, -4:4:100j, -4:4:100j]
kernel = sp.stats.gaussian_kde(points)
positions = np.vstack((x.ravel(), y.ravel(), z.ravel()))
density = np.reshape(kernel(positions).T, x.shape)

# now density is 100x100x100 ndarray

# plot points
ax = plt.subplot(projection='3d')
ax.plot(points[0,:], points[1,:], points[2,:], 'o')

# plot projection of density onto z-axis
plotdat = np.sum(density, axis=2)
plotdat = plotdat / np.max(plotdat)
plotx, ploty = np.mgrid[-4:4:100j, -4:4:100j]
ax.contour(plotx, ploty, plotdat, offset=-4)

ax.set_xlim((-4, 4))
ax.set_ylim((-4, 4))
ax.set_zlim((-4, 4))

Projection of contours onto z-axis

However, doing this for the other axes seems to be not implemented in Matplotlib. If I use the method outlined in this example, and specify a zdir keyword argument:

# plot projection of density onto x-axis
plotdat = np.sum(density, axis=0)
plotdat = plotdat / np.max(plotdat)
ploty, plotz = np.mgrid[-4:4:100j, -4:4:100j]
ax.contour(ploty, plotz, plotdat, offset=-4, zdir='x')

the generation of the contour is done 'along another slice', so to say:

enter image description here

Whereas I want something like this (bad Paint skills; hope the idea is clear):

enter image description here

One option I had in mind was to generate the contour along the default zdir='z' and then rotate the resulting curves in 3D space, but I have no idea how to approach this. I would be very grateful for any pointers!

like image 497
EelkeSpaak Avatar asked Apr 18 '16 17:04

EelkeSpaak


People also ask

How do you plot in 3D contour?

To plot 3D contour we will use countour3() to plot different types of 3D modules. Syntax: contour3(X,Y,Z): Specifies the x and y coordinates for the values in Z. contour3(Z): Creates a 3-D contour plot containing the isolines of matrix Z, where Z contains height values on the x-y plane.

How do you plot a 3D surface plot in Python?

We could plot 3D surfaces in Python too, the function to plot the 3D surfaces is plot_surface(X,Y,Z), where X and Y are the output arrays from meshgrid, and Z=f(X,Y) or Z(i,j)=f(X(i,j),Y(i,j)). The most common surface plotting functions are surf and contour. TRY IT!


1 Answers

I tried to modify the contour plots by mixing up the data calculated as a sum along an axis with the grid created by np.mgrid. I calculated the sum of the density along the axis on which I want to have the contour. This looks as follows:

# plot projection of density onto z-axis
plotdat = np.sum(density, axis=2)
plotdat = plotdat / np.max(plotdat)
plotx, ploty = np.mgrid[-4:4:100j, -4:4:100j]
ax.contour(plotx, ploty, plotdat, offset=-4, zdir='z')

#This is new
#plot projection of density onto y-axis
plotdat = np.sum(density, axis=1) #summing up density along y-axis
plotdat = plotdat / np.max(plotdat)
plotx, plotz = np.mgrid[-4:4:100j, -4:4:100j]
ax.contour(plotx, plotdat, plotz, offset=4, zdir='y')

#plot projection of density onto x-axis
plotdat = np.sum(density, axis=0) #summing up density along z-axis
plotdat = plotdat / np.max(plotdat)
ploty, plotz = np.mgrid[-4:4:100j, -4:4:100j]
ax.contour(plotdat, ploty, plotz, offset=-4, zdir='x')
#continue with your code

Unfortunately I'm not very familiar with the kernel density estimation, so I hope I did not understand something completely wrong but the result generated if you add the few lines of code above looks something similar than your fancy paint picture :) enter image description here

like image 53
jammartin Avatar answered Nov 15 '22 16:11

jammartin