Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I plot a projection of 3D scatter data on the XY/XZ/YZ planes?

I have 4D data (3D scatter points + color) plotted using matplotlib's mplot3d library. To aid in parsing how the cloud of points is distributed in space, I'd like to show a projection of the cloud across each of the 3 planes (XY, XZ, YZ) using a 2D histogram/contour plot.

Here is a MWE that uses ax.plot to do what I want (per the link below). This technically works, but I think replacing the buck-shot from ax.plot with the contour plots would be more visually pleasing:

import numpy as np

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Prepare sample data - normally distributed
NSamples = 5000
vmin, vmax = -2, 2

X = np.random.normal(loc=-.1, scale=.5, size=(NSamples,))
Y = np.random.normal(loc=.1, scale=.25, size=(NSamples,))
Z = np.random.normal(loc=0, scale=1, size=(NSamples,))

# Create figure, add subplot with 3d projection
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_xlim(vmin, vmax)
ax.set_ylim(vmin, vmax)
ax.set_zlim(vmin, vmax)

# Plot the data cloud
ax.scatter(X, Y, Z, s=.5, alpha=.05, color='k')

# Plot the 2D projections using `plot`. This is the piece I'd like to improve 
ax.plot(X, Y, '+', markersize=.2, color='r', zdir='z', zs=-2.)
ax.plot(X, Z, '+', markersize=.2, color='g', zdir='y', zs=2.)
ax.plot(Y, Z, '+', markersize=.2, color='b', zdir='x', zs=-2.)

plt.savefig("3DScatter.png")

# Now, I'd *like* for the following histograms to be plotted on each of the XY, XZ, YZ planes 
instead of using `plot` above
for label, data_x, data_y in [ ['XY', X, Y], ['XZ', X, Z], ['YZ', Y, Z] ]:
    hist, binx, biny = np.histogram2d( data_x, data_y, bins=[xbins, ybins])

    plt.figure(figsize=(5,5))
    plt.imshow(hist, extent=[vmin,vmax,vmin,vmax])
    plt.xlabel(label[1])

Which produces:

3D scatterXY

XZYZ

etc.

So to be clear, is there a way to plot the XY, XZ, YZ 2D histograms plotted with imshow above on the associated 3D axes? A contour-based solution would be fine as well.

Note that (I'm fairly certain) this is not a repeat of this related question, whose solution works only for 2D data (f(x,y)), not 3D (f(x,y,z)).

like image 267
paradiso Avatar asked May 17 '15 21:05

paradiso


1 Answers

If you are okay with using contour or contourf, you can do something like this:

import numpy as np

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Prepare sample data - normally distributed
NSamples = 5000
vmin, vmax = -2, 2

X = np.random.normal(loc=-.1, scale=.5, size=(NSamples,))
Y = np.random.normal(loc=.1, scale=.25, size=(NSamples,))
Z = np.random.normal(loc=0, scale=1, size=(NSamples,))

# Create figure, add subplot with 3d projection
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_xlim(vmin, vmax)
ax.set_ylim(vmin, vmax)
ax.set_zlim(vmin, vmax)

# Plot the data cloud
ax.scatter(X, Y, Z, s=.5, alpha=.05, color='k')

hist, binx, biny = np.histogram2d( X, Y)
x = np.linspace(X.min(), X.max(), hist.shape[0])
y = np.linspace(Y.min(), Y.max(), hist.shape[1])
x, y = np.meshgrid(x, y)
ax.contour(x, y, hist, zdir='z', offset=-3.)

hist, binx, biny = np.histogram2d( X, Z)
x = np.linspace(X.min(), X.max(), hist.shape[0])
z = np.linspace(Z.min(), Z.max(), hist.shape[1])
x, z = np.meshgrid(x, z)
ax.contour(x, hist, z, zdir='y', offset=3)

hist, binx, biny = np.histogram2d( Y, Z)
y = np.linspace(Y.min(), Y.max(), hist.shape[0])
z = np.linspace(Z.min(), Z.max(), hist.shape[1])
z, y = np.meshgrid(z, y)
ax.contour(hist, y, z, zdir='x', offset=-3)

ax.set_xlim([-3, 3])
ax.set_ylim([-3, 3])
ax.set_zlim([-3, 3])
like image 143
Julien Spronck Avatar answered Sep 20 '22 08:09

Julien Spronck