I am trying to "remove the floor" from a 3D surface plot. For example, in this matplotlib demo code:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
fig = plt.figure()
ax = fig.gca(projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)
ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3)
cset = ax.contour(X, Y, Z, zdir='z', offset=-100, cmap=cm.coolwarm)
cset = ax.contour(X, Y, Z, zdir='x', offset=-40, cmap=cm.coolwarm)
cset = ax.contour(X, Y, Z, zdir='y', offset=40, cmap=cm.coolwarm)
ax.set_xlabel('X')
ax.set_xlim(-40, 40)
ax.set_ylabel('Y')
ax.set_ylim(-40, 40)
ax.set_zlabel('Z')
ax.set_zlim(-100, 100)
I am trying to just get the top half of the 3d surface, without the blue floor and the bottom hump. I'd like them transparent.
I've tried setting vmin appropriately, and even using a masked array but I still get the "floor" of color in my plots.
Note: My real situation is plotting a KDE generated on some data, on a grid of points and I dont want the entire bottom of my plot to be the same blue color.
The idea can be to set the unwanted part to a transparent color, using a normalization of the colormap.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib.colors
fig = plt.figure()
ax = fig.gca(projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)
norm = matplotlib.colors.Normalize(0,100)
cmap = cm.coolwarm
cmap.set_under((0,0,0,0), alpha=0.0)
ax.plot_surface(X, Y, Z, rstride=8, cstride=8, norm=norm, cmap=cmap)
ax.set_xlabel('X')
ax.set_xlim(-40, 40)
ax.set_ylabel('Y')
ax.set_ylim(-40, 40)
ax.set_zlabel('Z')
ax.set_zlim(-100, 100)
plt.show()
Related
I have 3 contours, generated by the following:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy import stats
mean0 = [ 3.1627717, 2.74815376]
cov0 = [[0.44675818, -0.04885433], [-0.04885433, 0.52484173]]
mean1 = [ 6.63373967, 6.82700035]
cov1 = [[ 0.46269969, 0.11528141], [0.11528141, 0.50237073]]
mean2 = [ 7.20726944, 2.61513787]
cov2 = [[ 0.38486096, -0.13042758], [-0.13042758, 0.40928813]]
x = np.linspace(0, 10, 100)
y = np.linspace(0, 10, 100)
X, Y = np.meshgrid(x, y)
Z0 = np.random.random((len(x),len(y)))
Z1 = np.random.random((len(x),len(y)))
Z2 = np.random.random((len(x),len(y)))
def pdf0(arg1,arg2):
return (stats.multivariate_normal.pdf((arg1,arg2), mean0, cov0))
def pdf1(arg1,arg2):
return (stats.multivariate_normal.pdf((arg1,arg2), mean1, cov1))
def pdf2(arg1,arg2):
return (stats.multivariate_normal.pdf((arg1,arg2), mean2, cov2))
for i in range (0, len(x)):
for j in range(0,len(y)):
Z0[i,j] = pdf0(x[i],y[j])
Z1[i,j] = pdf1(x[i],y[j])
Z2[i,j] = pdf2(x[i],y[j])
Z0=Z0.T
Z1=Z1.T
Z2=Z2.T
fig3 = plt.figure()
ax3 = fig3.add_subplot(111)
ax3.contour(X,Y,Z0)
ax3.contour(X,Y,Z1)
ax3.contour(X,Y,Z2)
plt.show()
Which, visually, is plotted as the following:
I am wishing to plot all of these in a 3D plot, but when I try do so with:
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
# 3D plots for each contour.
surf1 = ax.plot_surface(X, Y, Z0, cmap=cm.coolwarm, linewidth=0, antialiased=False)
surf2 = ax.plot_surface(X, Y, Z1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
surf3 = ax.plot_surface(X, Y, Z2, cmap=cm.coolwarm, linewidth=0, antialiased=False)
ax.contour(X, Y, Z0, zdir='z', offset=-0.5)
ax.contour(X, Y, Z1, zdir='z', offset=-0.5)
ax.contour(X, Y, Z2, zdir='z', offset=-0.5)
ax.set_zlim(-0.5, 0.31)
plt.show()
The resulting graph is this:
How can I get the other two 3D contours to show nicely?
There is no general solution to this problem. Matplotlib cannot decide to show part of an object more in front than another part of it. See e.g. the FAQ, or other questions, like How to obscure a line behind a surface plot in matplotlib?
One may of course split up the object in several parts if necessary. Here, however, it seems sufficient to add the functions up.
surf1 = ax.plot_surface(X, Y, Z0+Z1+Z2, cmap=plt.cm.coolwarm,
linewidth=0, antialiased=False)
ax.contour(X, Y, Z0+Z1+Z2, zdir='z', offset=-0.5)
Is there a way to plot multiple plots in one window (graphics are displayed qt)?
Sure.
The keyword is subplot. Read this for a basic overview.
Just look at this official example from here:
from mpl_toolkits.mplot3d.axes3d import Axes3D
import matplotlib.pyplot as plt
# imports specific to the plots in this example
import numpy as np
from matplotlib import cm
from mpl_toolkits.mplot3d.axes3d import get_test_data
# Twice as wide as it is tall.
fig = plt.figure(figsize=plt.figaspect(0.5))
#---- First subplot
ax = fig.add_subplot(1, 2, 1, projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_zlim3d(-1.01, 1.01)
fig.colorbar(surf, shrink=0.5, aspect=10)
#---- Second subplot
ax = fig.add_subplot(1, 2, 2, projection='3d')
X, Y, Z = get_test_data(0.05)
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
plt.show()
Output
I would like to plot a surface with a colormap, wireframe and contours using matplotlib. Something like this:
Notice that I am not asking about the contours that lie in the plane parallel to xy but the ones that are 3D and white in the image.
If I go the naïve way and plot all these things I cannot see the contours (see code and image below).
import numpy as np
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
X, Y = np.mgrid[-1:1:30j, -1:1:30j]
Z = np.sin(np.pi*X)*np.sin(np.pi*Y)
ax.plot_surface(X, Y, Z, cmap="autumn_r", lw=0.5, rstride=1, cstride=1)
ax.contour(X, Y, Z, 10, lw=3, cmap="autumn_r", linestyles="solid", offset=-1)
ax.contour(X, Y, Z, 10, lw=3, colors="k", linestyles="solid")
plt.show()
If a add transparency to the surface facets then I can see the contours, but it looks really cluttered (see code and image below)
import numpy as np
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
X, Y = np.mgrid[-1:1:30j, -1:1:30j]
Z = np.sin(np.pi*X)*np.sin(np.pi*Y)
ax.plot_surface(X, Y, Z, cmap="autumn_r", lw=0.5, rstride=1, cstride=1, alpha=0.5)
ax.contour(X, Y, Z, 10, lw=3, cmap="autumn_r", linestyles="solid", offset=-1)
ax.contour(X, Y, Z, 10, lw=3, colors="k", linestyles="solid")
plt.show()
Question: Is there a way to obtain this result in matplotlib? The shading is not necessary, though.
Apparently it is a bug, if you try this
import numpy as np
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
X, Y = np.mgrid[-1:1:30j, -1:1:30j]
Z = np.sin(np.pi*X)*np.sin(np.pi*Y)
ax.plot_surface(X, Y, Z, cmap="autumn_r", lw=0, rstride=1, cstride=1)
ax.contour(X, Y, Z+1, 10, lw=3, colors="k", linestyles="solid")
plt.show()
And rotate around, you will see the contour lines disappearing when they shouldn't
I think you want to set the offset to the contour :
ax.contour(X, Y, Z, 10, offset=-1, lw=3, colors="k", linestyles="solid", alpha=0.5)
See this example for more:
http://matplotlib.org/examples/mplot3d/contour3d_demo3.html
And the docs here:
http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#contour-plots
offset: If specified plot a projection of the contour lines on this position in plane normal to zdir
Note, zdir = 'z' by default, but you can project in the x or y direction be setting the zdir accordingly.
I have setup mplot3d to provide a 3D surface plot per the example.
When I plot my data I am seeing that the surface is missing from a ridge running through the surface (see image). I noticed that surface filling appears to follow the stride but the grid-lines make viewing difficult at lower step sizes.
from mpl_toolkits.mplot3d import axes3d
from matplotlib import cm, pyplot
import numpy
Z = data[-300::]
X,Y = numpy.mgrid[:len(Z), :len(Z[0])]
fig = pyplot.figure(figsize=(20, 10), dpi=800)
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X,
Y,
Z,
rstride=len(Z)/5,
cstride=len(Z[0])/10,
alpha=.6,
linewidths=(.5,),
antialiased=True,
cmap=cm.coolwarm,
vmin=124,
vmax=186
)
cset = ax.contourf(X, Y, Z, zdir='z', offset=130, cmap=cm.coolwarm, vmin=124, vmax=186)
ax.set_xlim(len(Z) * 1.2, 0)
ax.set_ylim(0, len(Z[0]) * 1.2)
ax.elev = 25
ax.azim = 20
cb = fig.colorbar(surf, shrink=0.5, aspect=5)
Is there a way to fill the missing surface?
The only way i have found to accomplish this is by setting the stride to one and linewidth to 0. The downside to this is that I appear to lose the grid overlay.
surf = ax.plot_surface(X,
Y,
Z,
shade=True,
rstride=1, cstride=1, linewidth=0,
linewidths=(.5,),
antialiased=True,
)
I'm new to python and after installing it I've accomplished to plot my 3d data using matplotlib. Sadly the only thing I don't know how to get done is the color part. My image just shows the surface but doesn't use the color bar at all. Here is my code.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
from matplotlib.mlab import griddata
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
data = np.genfromtxt('Uizq.txt')
x = data[:,0]
y = data[:,1]
z = data[:,2]
xi = np.linspace(min(x), max(x))
yi = np.linspace(min(y), max(y))
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('U')
X, Y = np.meshgrid(xi, yi)
Z = griddata(x, y, z, xi, yi)
ax.set_zlim3d(np.min(Z), np.max(Z))
surf = ax.plot_surface(X, Y, Z, rstride=2, cstride=2, cmap=cm.jet,
linewidth=0.5, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
you can obviously see that it is all blue, and I want to relate the color with "U" using the full cm.jet spectrum. This might be a very noob question, so sorry if you rolled your eyes.
Add the line
surf.set_clim([np.min(Z),np.max(Z)])
before you add the color bar.
It seems that the 3D plotting does not take into account the masking, so you are including NaN in the data, which confuses the automatic color limits.