Matplotlib 3D Scatter Plot with Colorbar - python

Borrowing from the example on the Matplotlib documentation page and slightly modifying the code,
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zl, zh)
cs = randrange(n, 0, 100)
ax.scatter(xs, ys, zs, c=cs, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
Will give a 3D scatter plot with different colors for each point (random colors in this example). What's the correct way to add a colorbar to the figure, since adding in plt.colorbar() or ax.colorbar() doesn't seem to work.

This produces a colorbar (though possibly not the one you need):
Replace this line:
ax.scatter(xs, ys, zs, c=cs, marker=m)
with
p = ax.scatter(xs, ys, zs, c=cs, marker=m)
then use
fig.colorbar(p)
near the end

Using the above answer did not solve my problem. The colorbar colormap was not linked to the axes (note also the incorrect colorbar limits):
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
data = np.random.rand(3, 100)
x, y, z = data # for show
c = np.arange(len(x)) / len(x) # create some colours
p = ax.scatter(x, y, z, c=plt.cm.magma(0.5*c))
ax.set_xlabel('$\psi_1$')
ax.set_ylabel('$\Phi$')
ax.set_zlabel('$\psi_2$')
ax.set_box_aspect([np.ptp(i) for i in data]) # equal aspect ratio
fig.colorbar(p, ax=ax)
The solution (see here also) is to use cmap in ax.scatter:
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
data = np.random.rand(3, 100)
x, y, z = data # for show
c = np.arange(len(x)) / len(x) # create some colours
p = ax.scatter(x, y, z, c=0.5*c, cmap=plt.cm.magma)
ax.set_xlabel('$\psi_1$')
ax.set_ylabel('$\Phi$')
ax.set_zlabel('$\psi_2$')
ax.set_box_aspect([np.ptp(i) for i in data]) # equal aspect ratio
fig.colorbar(p, ax=ax)

Related

Is there a way to smooth a line between points such that the gradient at each point is 0?

Is there a way to smooth this line between the points such that the line gradient at each point is 0 (as if there were a cubic function between the points, with each data-point as a turning point).
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = [1,2,3,4,5,6,7,8,9,10]
y = [8,2,1,7,5,5,8,1,9,5]
ax.plot(x,y)
'Unsmoothed' plot:
I am not sure if the use case is desirable, but you can use a spline interpolation with scipy.interpolate.CubicSpline:
import numpy as np
from scipy.interpolate import CubicSpline
cs = CubicSpline(x, y)
xs = np.linspace(min(x), max(x), num=100)
fig, ax = plt.subplots()
ax.plot(x, y, label='data', marker='o')
ax.plot(xs, cs(xs), label='spline')
ax.legend()
output:
alternative: CubicHermiteSpline
import numpy as np
from scipy.interpolate import CubicHermiteSpline
cs = CubicHermiteSpline(x, y, np.zeros(len(x))) # force gradient to zero
xs = np.linspace(min(x), max(x), num=100)
fig, ax = plt.subplots()
ax.plot(x, y, label='data', marker='o')
ax.plot(xs, cs(xs), label='spline')
ax.legend()
with threshold (only for "nice" display purposes)
import numpy as np
from scipy.interpolate import CubicHermiteSpline
g = np.gradient(y)/np.gradient(x)
g = np.where(abs(g)>2, g, 0)
cs = CubicHermiteSpline(x, y, g)
xs = np.linspace(min(x), max(x), num=100)
fig, ax = plt.subplots()
ax.plot(x, y, label='data', marker='o')
ax.plot(xs, cs(xs), label='spline')
ax.legend()
output:

subplots forced to have same axis

Recently I am plotting 2 graphs at a fig. The data is different and share no common contents. But the final visualizations are forced to have the same axis, I don't understand.
Image here
#################################################################################################
fig, (ax1,ax2) = plt.subplots(1,2, sharey = False, sharex = False)
c = list(len(mydf)*'b')
for i in range(len(c)):
if mydf['percent'][i] > 0.05:
c[i] = 'r'
# ax1 = fig.add_subplot(121)
ax1.bar(range(len(mydf['cdf'])), mydf['cdf'], color = c)
ax1.set_xticks(range(len(mydf['cdf'])))
ax1.set_xticklabels(list(mydf['3D_Attri']), rotation=45)
###########################################################################################3
ax2 = fig.add_subplot(122, projection='3d')
xs = mydf['sphere']
ys = mydf['cylinder']
zs = mydf['addition']
ax2.scatter(xs, ys, zs, zdir='z', s=20, c=c, depthshade=True)
ax2.set_xlabel('sphere')
ax2.set_ylabel('cylinder')
ax2.set_zlabel('addition')
plt.show()
The problem is that you create two subplots in your first line of code. Place a plt.show() directly after that to see that there the wrong axis is already plotted. This will interfere with your 3D graph later, which you simply place on top of it. You have to approach this differently:
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
##################################################################################
fig = plt.figure()
c = list(len(mydf)*'b')
for i in range(len(c)):
if mydf['percent'][i] > 0.05:
c[i] = 'r'
ax1 = fig.add_subplot(121)
ax1.bar(range(len(mydf['cdf'])), mydf['cdf'], color = c)
ax1.set_xticks(range(len(mydf['cdf'])))
ax1.set_xticklabels(list(mydf['3D_Attri']), rotation=45)
##################################################################################
ax2 = fig.add_subplot(122, projection='3d')
xs = mydf['sphere']
ys = mydf['cylinder']
zs = mydf['addition']
ax2.scatter(xs, ys, zs, zdir='z', s=20, c=c, depthshade=True)
ax2.set_xlabel('sphere')
ax2.set_ylabel('cylinder')
ax2.set_zlabel('addition')
plt.show()
Output from a toy data set:

How to plot heatmap colors in 3D in Matplotlib

I am using Matplotlib 3D to plot 3 dimensions of my dataset like below:
But now I also want to visualize a 4th dimension (which is a scalar value between 0 to 20) as a heatmap. So basically, I want each point to take it's color based on this 4th dimension's value.
Is there such a thing exists in Matplotlib? How can I convert a bunch of numbers between [0-20] to heatmap colors?
I took the code from here: http://matplotlib.org/mpl_examples/mplot3d/scatter3d_demo.py
Yes, something like this:
update here is a version with a colorbar.
import numpy as np
from pylab import *
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111,projection='3d')
n = 100
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, 0, 100)
colmap = cm.ScalarMappable(cmap=cm.hsv)
colmap.set_array(zs)
yg = ax.scatter(xs, ys, zs, c=cm.hsv(zs/max(zs)), marker='o')
cb = fig.colorbar(colmap)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
looks like:
update Here is an explicit example of coloring your data points by some 4th dimensional attribute.
import numpy as np
from pylab import *
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111,projection='3d')
n = 100
xs = randrange(n, 0, 100)
ys = randrange(n, 0, 100)
zs = randrange(n, 0, 100)
the_fourth_dimension = randrange(n,0,100)
colors = cm.hsv(the_fourth_dimension/max(the_fourth_dimension))
colmap = cm.ScalarMappable(cmap=cm.hsv)
colmap.set_array(the_fourth_dimension)
yg = ax.scatter(xs, ys, zs, c=colors, marker='o')
cb = fig.colorbar(colmap)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
I mostly agree with #seth but I would like to change 1 line so that the color values are not normalized:
yg = ax.scatter(xs, ys, zs, c=colmap.to_rgba(the_fourth_dimension)[:,0,0:3], marker='o')

Any easy way to plot a 3d scatter in Python that I can rotate around?

Currently I'm using matplotlib to plot a 3d scatter and while it gets the job done, I can't seem to find a way to rotate it to see my data better.
Here's an example:
import pylab as p
import mpl_toolkits.mplot3d.axes3d as p3
#data is an ndarray with the necessary data and colors is an ndarray with
#'b', 'g' and 'r' to paint each point according to its class
...
fig=p.figure()
ax = p3.Axes3D(fig)
ax.scatter(data[:,0], data[:,2], data[:,3], c=colors)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fig.add_axes(ax)
p.show()
I'd like a solution that lets me do it during execution time but as long as I can rotate it and it's short/quick I'm fine with it.
Here's a comparison of the plots produced after applying a PCA to the iris dataset:
1. mayavi
2. matplotlib
Mayavi makes it easier to visualize the data, but MatPlotLib looks more professional. Matplotlib is also lighter.
Well, first you need to define what you mean by "see my data better"...
You can rotate and zoom in on the plot using the mouse, if you're wanting to work interactively.
If you're just wanting to rotate the axes programatically, then use ax.view_init(elev, azim) where elev and azim are the elevation and azimuth angles (in degrees) that you want to view your plot from.
Alternatively, you can use the ax.elev, ax.azim, and ax.dist properties to get/set the elevation, azimuth, and distance of the current view point.
Borrowing the source from this example:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zl, zh)
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
We get a nice scatterplot:
You can rotate the axes programatically as shown:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zl, zh)
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
ax.azim = 200
ax.elev = -45
plt.show()
Hope that helps a bit!
Using mayavi, you can create such a plot with
import enthought.mayavi.mlab as mylab
import numpy as np
x, y, z, value = np.random.random((4, 40))
mylab.points3d(x, y, z, value)
mylab.show()
The GUI allows rotation via clicking-and-dragging, and zooming in/out via right-clicking-and-dragging.

Plotting 3D scatter with python?

Is there any module that could aid me in producing something like this?
Like this, say?
(source: sourceforge.net)
The matplotlib examples gallery is a wonderful thing to behold.
Code copied from the linked example.
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def randrange(n, vmin, vmax):
return (vmax-vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zl, zh)
ax.scatter(xs, ys, zs, c=c, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
Adapted from the Cookbook
from numpy import *
import pylab as p
import mpl_toolkits.mplot3d.axes3d as p3
x=random.randn(100)
y=random.randn(100)
z=random.randn(100)
fig=p.figure()
ax = p3.Axes3D(fig)
ax.scatter3D(ravel(x),ravel(y),ravel(z))
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
p.show()
I think matplotlib should be able to do something like that.

Categories