Matplotlib 3DPlot Extra Lines when Dimensions not Equal - python
Consider this MWE:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib import cm
n = 15
m = 12
x = np.linspace(-5, 5, n)
y = np.linspace(-5, 5, m)
Z = np.zeros((m, n))
for i in xrange(m):
for j in xrange(n):
Z[i, j] = x[j]**2 + y[i]**2
### Plot surface ###
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X, Y = np.meshgrid(x, y)
ax.plot_surface(X, Y, Z)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('Z')
plt.show()
Note in particular that the dimensions n and m are not equal. The resulting plot has some weird lines hanging down, as well as strange coloring:
What's going on here, and how can I prevent this?
Unlike 2D, 3D plots in matplotlib have a lot of shortcomings. Let me quote one of the answers in matplotlib FAQ:
This is probably the most commonly reported issue with mplot3d. The
problem is that – from some viewing angles – a 3D object would appear
in front of another object, even though it is physically behind it.
This can result in plots that do not look “physically correct.”
Unfortunately, while some work is being done to reduce the occurance
of this artifact, it is currently an intractable problem, and can not
be fully solved until matplotlib supports 3D graphics rendering at its
core.
The problem occurs due to the reduction of 3D data down to 2D +
z-order scalar. A single value represents the 3rd dimension for all
parts of 3D objects in a collection. Therefore, when the bounding
boxes of two collections intersect, it becomes possible for this
artifact to occur. Furthermore, the intersection of two 3D objects
(such as polygons or patches) can not be rendered properly in
matplotlib’s 2D rendering engine.
This problem will likely not be solved until OpenGL support is added
to all of the backends (patches are greatly welcomed). Until then, if
you need complex 3D scenes, we recommend using MayaVi.
For your particular problem (and notice that I don't think this has anything to do with different sizes in each direction) I would advise you to increase your surface shape (even if artificially) and play around with the number of strides until you obtain something that is satisfactory:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib import cm
n = 150
m = 120
x = np.linspace(-5, 5, n)
y = np.linspace(-5, 5, m)
Z = np.zeros((m, n))
for i in range(m):
for j in range(n):
Z[i, j] = x[j]**2 + y[i]**2
### Plot surface ###
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X, Y = np.meshgrid(x, y)
ax.plot_surface(X, Y, Z,rstride=1, cstride=1)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('Z')
plt.show()
,which results in this:
The example above give rstrides and cstrides a value of 10. Should you increase it too much (let's say 80) and the problem becomes obvious:
Other option is for you to follow the recommendation of matplotlib FAQ itself and check Mayavi. Notice, however, that mayavi still does not support Python 3. Personally, if you need something quick to work with, I would recommend PyQtGraph.
Related
3D surface/volume plot of list
I would like to represent a set of 3D points as a surface. The points are in an array format for x, y, z. I managed to plot the points in 3D in a sub-optimal solution by adding an array c which just contains 1's for each (x,y,z). There's surely a better solution for that already. What I want to do now is connect the points somehow and fill out a volume which contains these points in 3D. import numpy as np import matplotlib.pyplot as plt x = np.array([3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8]) y = np.array([15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,21,21,21,15,15,15,15,15,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,26,26,17,17,18,18,18,18,18,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,20,20,20,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,33,33,22,23,23,23,23,24,24,24,24,24,24,24]) z = np.array([5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,8,9,10,1,2,3,4,5,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,4,5,6,7,8,9,10,9,10,1,2,1,2,3,4,5,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,6,7,8,9,10,1,2,3,1,2,3,4,5,6,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,1,2,3,4,1,2,3,4,5,6,7]) c = np.array([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') img = ax.scatter(x, y, z, c=c) plt.show() This is what I get: What I approximately try to get: So basically I'm looking for something that encloses all my points. I hope this is not a duplicate, at least I did not find any answer that worked out for me. Edit: Just added Mayavi to the tags, since I'm trying to find a workaround with the package.
Scatter plot over 2D-histogram in matplotlib with log-scale
I have two sets of points with values (x, y). One is enormous (300k) and one is small (2k). I want to show a scatter plot of the latter over a 2D-histogram of the former in log-log scale. plt.xscale('log')-like commands keep messing up the histogram and when I just take logs of x's and y's and then do all the plotting, my ticks are say -3 not 10^-3 and the pretty logarithmic minor ticks are missing altogether. What's the most elegant solution in matplotlib? Do I have to dig into the artist layer?
If you forgive a bit of self-advertisement, you may use my library physt (see https://github.com/janpipek/physt). Then, you can write code like this: import numpy as np import matplotlib.pyplot as plt from physt import h2 # Data r1 = np.random.normal(0, 1, 20000) r2 = np.random.normal(0, .3, 20000) + r1 x = np.exp(r1) y = np.exp(r2) # Plot scatter fig, ax = plt.subplots() ax.scatter(x[:1000], y[:1000], s=2) H = h2(x, y, "exponential") H.plot(ax=ax, zorder=-1) # Necessary to put behind Which, I hope is the solution to your problem:
Plotting 1-D dataset with radial symmetry around the origin in Python
This is likely a very basic question, but my python knowledge is somewhat limited and I've had some trouble deciphering other questions+answers, so I'm hoping someone here can help... I have a set of 1-D data points (the number of molecules at each point along a radius, essentially) that I want to plot as a 2-D radially symmetrical image. I want to rotate it around an origin (0,0). Basically, I have something like but I want something like I've seen this done relatively easily with functions or defined vectors (i.e. with scipy's interpolate module), but I don't have a function or randomly-generated data -- I have a list. I know there must be a simple way to do this, but I'd really appreciate it if someone could point me in the right direction! I've included a really really small example dataset (plotted as a line in log scale) for people to play with if they feel so inclined: import numpy as np import matplotlib.pyplot as plt rad25 = np.array([25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]) O_rad25 = np.array([1.01E+15,3.00E+14,1.20E+14,5.63E+13,2.90E+13,1.59E+13,9.21E+12,5.53E+12,3.43E+12,2.18E+12,1.42E+12,9.44E+11,6.38E+11,4.37E+11,3.03E+11,2.13E+11,1.51E+11,1.08E+11,7.77E+10,5.60E+10,4.02E+10,2.84E+10,1.94E+10,1.20E+10,5.78E+09]) plt.plot(rad25,O_rad25) plt.yscale('log') plt.xlabel('Radius (distance from center in um)') plt.ylabel('Number of molecules') plt.show()
You need to create an array of values from 0 - 360 degrees, and then create a meshgrid from this array and your array of values. This can then be plotted on a radially projected subplot. Something like this, but with your data of cause: import numpy as np import matplotlib.pyplot as plt # Swap out with your data radialVals = np.linspace(0,1) azm = np.linspace(0, 2 * np.pi) r, th = np.meshgrid(radialVals, azm) z = (r ** 2.0) / 4.0 plt.subplot(projection="polar") plt.pcolormesh(th, r, z) plt.plot(azm, r, ls='none') plt.grid() plt.show()
I borrowed some from Jon's answer (his is very nearly correct), and added a colorbar to illustrate, but this should get you what you want. Since you know your radius and that your data has radial symmetry, a polar plot is the natural choice. import numpy as np import matplotlib.pyplot as plt from matplotlib import colors fig, ax = plt.subplots(subplot_kw=dict(projection='polar')) azm = np.linspace(0, 2 * np.pi) r, th = np.meshgrid(rad25, azm) z = np.tile(O_rad25, (r.shape[0], 1)) plt.pcolormesh(th, r, z, norm=colors.LogNorm(O_rad25.min(), O_rad25.max())) plt.colorbar(label='Number of Molecules')
Matplotlib: multiple 3D lines all get drawn using the final y-value in my loop
I am trying to plot multiple lines in a 3D figure. Each line represents a month: I want them displayed parallel in the y-direction. My plan was to loop over a set of Y values, but I cannot make this work properly, as using the ax.plot command (see working code below) produces a dozen lines all at the position of the final Y value. Confusingly, swapping ax.plot for ax.scatter does produce a set of parallel lines of data (albeit in the form of a set of dots; ax.view_init set to best display the parallel aspect of the result). How can I use a produce a plot with multiple parallel lines? My current workaround is to replace the loop with a dozen different arrays of Y values, and that can't be the right answer. from mpl_toolkits.mplot3d.axes3d import Axes3D import matplotlib.pyplot as plt import numpy as np # preamble fig = plt.figure() ax = fig.add_subplot(111, projection='3d') cs = ['r','g','b','y','r','g','b','y','r','g','b','y'] # x axis X = np.arange(24) # y axis y = np.array([15,45,75,105,135,165,195,225,255,285,315,345]) Y = np.zeros(24) # data - plotted against z axis Z = np.random.rand(24) # populate figure for step in range(0,12): Y[:] = y[step] # ax.plot(X,Y,Z, color=cs[step]) ax.scatter(X,Y,Z, color=cs[step]) ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') # set initial view of plot ax.view_init(elev=80., azim=345.) plt.show() I'm still learning python, so simple solutions (or, preferably, those with copious explanatory comments) are greatly appreciated.
Use ax.plot(X, np.array(Y), Z, color=cs[step]) or Y = [y[step]] * 24 This looks like a bug in mpl where we are not copying data when you hand it in so each line is sharing the same np.array object so when you update it all of your lines.
Create a stack of polar plots using Matplotlib/Python
I need to generate a stack of 2D polar plots (a 3D cylindrical plot) so that I can view a distorted cylinder. I want to use matplotlib since I already have it installed and want to distribute my code to others who only have matplotlib. For example, say I have a bunch of 2-D arrays. Is there any way I can do this without having to download an external package? Here's my code. #!usr/bin/env python import matplotlib.pyplot as plt import numpy as np x = np.arange(-180.0,190.0,10) theta = (np.pi/180.0 )*x # in radians A0 = 55.0 offset = 60.0 R = [116.225,115.105,114.697,115.008,115.908,117.184,118.61,119.998,121.224,122.216,\ 122.93,123.323,123.343,122.948,122.134,120.963,119.575,118.165,116.941,116.074,115.66\ ,115.706,116.154,116.913,117.894,119.029,120.261,121.518,122.684,123.594,124.059,\ 123.917,123.096,121.661,119.821,117.894,116.225] fig = plt.figure() ax = fig.add_axes([0.1,0.1,0.8,0.8],polar=True) # Polar plot ax.plot(theta,R,lw=2.5) ax.set_rmax(1.5*(A0)+offset) plt.show() I have 10 more similar 2D polar plots and I want to stack them up nicely. If there's any better way to visualize a distorted cylinder in 3D, I'm totally open to suggestions. Any help would be appreciated. Thanks!
If you want to stack polar charts using matplotlib, one approach is to use the Axes3D module. You'll notice that I used polar coordinates first and then converted them back to Cartesian when I was ready to plot them. from numpy import * from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt n = 1000 fig = plt.figure() ax = fig.gca(projection='3d') for k in linspace(0, 5, 5): THETA = linspace(0, 2*pi, n) R = ones(THETA.shape)*cos(THETA*k) # Convert to Cartesian coordinates X = R*cos(THETA) Y = R*sin(THETA) ax.plot(X, Y, k-2) plt.show() If you play with the last argument of ax.plot, it controls the height of each slice. For example, if you want to project all of your data down to a single axis you would use ax.plot(X, Y, 0). For a more exotic example, you can map the height of the data onto a function, say a saddle ax.plot(X, Y, -X**2+Y**2 ). By playing with the colors as well, you could in theory represent multiple 4 dimensional datasets (though I'm not sure how clear this would be). Examples below: