Is there an equivalent to "Axes3DSubplot.plot_surface" in 2D ?
I am trying to plot the projection of a mesh on the XY-plane in matplotlib (so not in '3d' mode).
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Data (wireframe sphere)
theta, phi = np.meshgrid( np.linspace(0, np.pi/2, 10), np.linspace(0, np.pi/2, 10) )
x = np.sin(theta) * np.cos(phi)
y = np.sin(theta) * np.sin(phi)
z = np.cos(theta)
fig = plt.figure()
# Subplot 3D
ax1 = fig.add_subplot(1, 2, 1, projection='3d', aspect='equal')
colors = matplotlib.cm.jet(np.hypot(x,y))
surface = ax1.plot_surface(x, y, z, rstride=1, cstride=1, facecolors = colors, alpha=0.5 )
projection = ax1.plot_surface(0, y, z, rstride=1, cstride=1, facecolors = colors )
projection.set_edgecolor('k')
# Subplot 2D
ax2 = fig.add_subplot(1, 2, 2, aspect='equal')
ax2.plot(y, z, 'k')
ax2.plot(y.T, z.T, 'k')
I am trying to produce a similar result than :
ax1.plot_surface(0, y, z, rstride=1, cstride=1, facecolors = colors )
But in the 2D subplot. I cannot find an equivalent for plot_surface in the doc of AxesSubplot. The only thing I managed to do is plot the wireframe (but not the facecolors) with :
ax2.plot(y, z, 'k')
ax2.plot(y.T, z.T, 'k')
I cannot upload an image, but basically, I want to put the "colors" in the second subplot.
Thanks,
EDIT:
#Tim
Yeah, I suppose, in this case, I managed to do it with :
ax2.contourf(y, z, np.hypot(x,y), levels=np.hypot(x,y)[0], cmap=matplotlib.cm.jet)
In a more generic case, you'll need the right level-function and a bit of tweaking with the levels and colormap, but it seems doable.
Another solution would be to use matplotlib.patches.Polygon to draw each projected face.
You can use contourf to produce coloured 2D contours:
# Subplot 2D
ax2 = fig.add_subplot(1, 2, 2, aspect='equal')
ax2.contourf(x, y, z, facecolors=colors)
Although this doesn't seem to be exactly what you need, it's a step in the right direction.
Related
A grid is interpolated with scipy's griddata() and contains values and NaNs. However, when the grid is plotted with mplot3d's plot_surface() there are gaps in the surface (see upper plot). The lower the view height, the more of such gaps are rendered. When the grid is plotted in 2D with imshow() there are no gaps (see lower plot).
Comparison plot_surface and imshow
Here is a close-up:
I couldn't find an answer to this problem and I am wondering whether this is a known issue.
This small example reproduces the problem: (data.txt)
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
Z = np.loadtxt('data.txt')
X = np.linspace(0,3,301)
Y = np.linspace(0,3,301)
X = np.array([X,]*(301)).transpose()
Y = np.array([Y,]*(301))
fig = plt.figure(figsize=(15,15), dpi=100)
ax = fig.add_subplot(2, 1, 1, projection='3d')
plt.hold(True)
ax.view_init(40,300)
surf = ax.plot_surface(X, Y, Z, cmap='jet', rstride=1, cstride=1, antialiased=False, shade=False, alpha=1.0, linewidth=0, vmin=0.25, vmax=0.35);
ax.invert_yaxis()
ax.dist = 11
ax = fig.add_subplot(2, 1, 2)
plt.hold(True)
plt.imshow(Z, cmap='jet', vmin=0.25, vmax=0.35, origin='lower');
plt.show()
I am trying to render a surface, but I do not manage to get a pretty visualisation. The plot_surface function from matplotlib gives me the following figure:
produced by the code below. How do I get rid of this transparency and the wireframe that is still visible if you look carefully?
import numpy as np
import matplotlib.pyplot as pl
from mpl_toolkits.mplot3d import Axes3D
pl.ion()
nx = 512
ny = 512
Lx = 2.e6
Ly = 2.e6
x = np.linspace(0., Lx, nx)
y = np.linspace(0., Ly, ny)
xx, yy = np.meshgrid(x,y)
Ld = 6.e4
h = np.exp(-( (xx - 0.5*Lx)**2 + (yy - 0.5*Ly)**2) / Ld**2 )
pl.figure()
ax = pl.subplot(111, projection='3d')
ax.plot_surface(xx/1000., yy/1000., h, alpha=1., cstride=1, rstride=1, linewidth=0)
ax.set_zlim3d(-0.2, 1.)
It's only a workaround, but this works for most matplotlib routines like e.g. contourf (where I had the same problem before); calling the plot routine (in this case plot_surface) twice solves both problems:
The left figure is with calling plot_surface once, the right one calling it twice.
For a non-transparent surface, setting antialiased=False helps (left figure below), with transparency antialiased=True produces very thin lines at the polygon edges (I suspect because the polygons slightly overlap), but they are hardly visible (right figure below).
fig = pl.figure()
ax = pl.subplot(121, projection='3d')
surf = ax.plot_surface(xx/1000., yy/1000., h, alpha=1.0, cstride=1, rstride=1, linewidth=0, antialiased=False)
ax = pl.subplot(122, projection='3d')
surf = ax.plot_surface(xx/1000., yy/1000., h, alpha=0.3, cstride=1, rstride=1, linewidth=0, antialiased=True)
I am trying to have two data values drawn on the same set of 3d axis. The value of the two data sets can differ by 1 or 2 orders of magnitude. As a result I want two Z axis similar to the twinx or twiny commands for 2d plots. A rough example is shown in the code below
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
result=[['1', '2', '3'],
['4', '5', '6'],
['7', '8', '9']]
result = np.array(result, dtype=np.int)
fig=plt.figure(figsize=(5, 5), dpi=150)
ax1=fig.add_subplot(111, projection='3d')
xlabels = np.array(['data1x', 'data2x', 'data3x'])
xpos = np.arange(xlabels.shape[0])
ylabels = np.array(['data1y','data2y','data3y'])
ypos = np.arange(ylabels.shape[0])
xposM, yposM = np.meshgrid(xpos, ypos, copy=False)
zpos=result
zpos = zpos.ravel()
dx=0.5
dy=0.5
dz=zpos
ax1.w_xaxis.set_ticks(xpos + dx/2.)
#ax1.w_xaxis.set_ticklabels(xlabels)
ax1.w_yaxis.set_ticks(ypos + dy/2.)
#ax1.w_yaxis.set_ticklabels(ylabels)
values = np.linspace(0.2, 1., xposM.ravel().shape[0])
colors = cm.rainbow(values)
ax1.bar3d(xposM.ravel(), yposM.ravel(), dz*0, dx, dy, dz, color=colors)
X = np.arange(-1, 1, 0.25)
Y = np.arange(-1, 1, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = 100*np.sin(R)
surf = ax1.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
plt.show()
How can i change the z scale for one of the plots so I can better see the features of both?
You can scale the data from one of the arrays to correspond to the magnitude of the other. Then, when plotted, the z-extend will be comparable.
In order to inlcude a colorbar for the scaled data that shows the original data range, I used a second set of axis.
Import the following additional libraries:
import matplotlib as mpl # for general access to the colorbar class
import matplotlib.gridspec as gridspec # to set up an axis-grid
Set up your axis:
gs = gridspec.GridSpec(1, 2,
width_ratios=[20,1],
)
ax1 = fig.add_subplot(gs[0], projection='3d')
ax2 = fig.add_subplot(gs[1])
You can adjust the width-ratios to change the width of the colorbar (given by ax2) relative to the data plot (given by ax1).
use numpy's amax to determine the maxima of your two data sets for the scaling (which can be taken care of when calling the surface plot:
surf = ax1.plot_surface(X, Y, Z/np.amax(Z)*np.amax(zpos),
rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
using Z/np.amax(Z)*np.amax(zpos) will scale your Z-data to the magnitude of zpos.
Now, plot a colorbar on the second axis:
cbar = mpl.colorbar.ColorbarBase(ax2, cmap = cm.coolwarm,
norm=mpl.colors.Normalize(vmin=np.amin(Z), vmax=np.amax(Z)))
To ensure that the colorbar covers the range of the Z-date, use the norm functionality.
This is your plot:
You can place second - transparent - axes over the first ones and use shared x and y axes.
Set up a second set of axes, including sharex and sharey:
ax2 = fig.add_axes(ax1.get_position(), projection='3d',
sharex=ax1, sharey=ax1)
Make the background transparent and remove the actual axes lines (and ticks, etc.):
ax2.set_axis_off()
ax2.patch.set_facecolor('none')
Plot as before, but specify the second axes (ax2), you can also plot a colorbar without any additional effort:
surf = ax2.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
cb = fig.colorbar(surf,ax=ax1)
Set xlim and ylim to avoid any misalignment (this can probably be linked to the data):
ax1.set_xlim([-1.0, 3.0])
ax1.set_ylim([-1.0, 3.0])
The resulting plot will be:
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 am trying to make a 3D plot from x, y, z points list, and I want to plot color depending on the values of a fourth variable rho.
Currently I have ;
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot3D(cell_x, cell_y, cell_z, linestyle='None', marker='o', markersize = 5, antialiased=True)
ax.set_xlim3d(0.45, 0.55)
ax.set_ylim3d(0.45, 0.55)
ax.set_zlim3d(0.45, 0.55)
How to add cell_rho (my fourth array) as the color of my x, y, z points ? (for example for a jet colormap).
Thank you very much.
EDIT : I can't use scatter plots because for my 18000 points scatter plots are very slow compared to plot3d with markers only.
If you want to display a simple 3D scatterplot, can't you just use scatter?
E.g.,
x, y, z = randn(100), randn(100), randn(100)
fig = plt.figure()
from mpl_toolkits.mplot3d import Axes3D
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x, y, z, c=randn(100))
plt.show()
(I'm running the above code under python -pylab.)
It seems, on the contrary, that with plot3D you must convert your fourth dimension to RGB tuples.