I am trying to rotate a figure horizontally in python. With the mouse I am only able to rotate through one degree of freedom as well as being able to spin the figure, for example this is what I can do at the momement:
These movements are only vertical. I would like to be able to rotate the figure in all directions, for example:
This would allow me to put the figure on its side etc. However at the moment I cannot do this, all I can do it rotate along one degree of freedom.
Here is my code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
import math
fig = plt.figure()
ax = fig.gca(projection='3d')
nphi,nz= 13, 101
r=1
phi = np.linspace(0,360, nphi)/180.0*np.pi
z= np.linspace(0,350,nz)
cols=[]
verts2 = []
for i in range(len(phi)-1):
cp0= r*np.cos(phi[i])
cp1= r*np.cos(phi[i+1])
sp0= r*np.sin(phi[i])
sp1= r*np.sin(phi[i+1])
for j in range(len(z)-1):
z0=z[j]
z1=z[j+1]
verts=[]
verts.append((cp0, sp0, z0))
verts.append((cp1, sp1, z0))
verts.append((cp1, sp1, z1))
verts.append((cp0, sp0, z1))
verts2.append(verts)
col=plt.cm.Blues(0.4)
cols.append(col)
poly3 = Poly3DCollection(verts2, facecolor=cols ,edgecolor = "red" )
poly3.set_alpha(0.5)
ax.add_collection3d(poly3)
ax.set_xlabel('X')
ax.set_xlim3d(-3, 3)
ax.set_ylabel('Y')
ax.set_ylim3d(-3, 3)
ax.set_zlabel('Z')
ax.set_zlim3d(0, 300)
plt.axis('off')
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
plt.show()
Unfortunately, I don't think you can; From the documentation:
‘elev’ stores the elevation angle in the z plane. ‘azim’ stores the
azimuth angle in the x,y plane
So you've not actually got 3 degrees of freedom here, because you can only rotate in the xy plane, rather than around each axis independently.
It's a bit clearer if you display the axes:
Related
How can we make spheres of radius R centered at given coordinates(x,y,z). Like if there are 10 set of coordinates for the centers of the spheres and correspondingly 10 different values of the radii. How can we plot it in python ? Is there any way to do this in python ,like in MATLAB this can be done using surf command. In python how can we achieve this ?
This will solve your problem
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
list_center = [(1,2,3),(-4,-5,6), (5,5,6)]
list_radius = [1,2,1]
def plt_sphere(list_center, list_radius):
for c, r in zip(list_center, list_radius):
ax = fig.gca(projection='3d')
# draw sphere
u, v = np.mgrid[0:2*np.pi:50j, 0:np.pi:50j]
x = r*np.cos(u)*np.sin(v)
y = r*np.sin(u)*np.sin(v)
z = r*np.cos(v)
ax.plot_surface(x-c[0], y-c[1], z-c[2], color=np.random.choice(['g','b']), alpha=0.5*np.random.random()+0.5)
fig = plt.figure()
plt_sphere(list_center, list_radius)
I am trying to plot this radiation pattern figure
of the following equation:
I can make it partially:
%matplotlib inline
import matplotlib.ticker
import matplotlib.pyplot as plt
plt.style.use('seaborn-white')
import numpy as np
import pylab as pl
def gain_dip(theta, phi):
return 1.641*(np.cos(np.pi/2*np.cos(theta))/np.sin(theta))**2
theta = np.arange(-np.pi, np.pi,0.01)
# plot
ax = plt.subplot(111, polar=True)
# set zero west
ax.set_theta_zero_location('W')
ax.set_theta_direction('clockwise')
# let set an azimuth for example, pi
plt.plot(theta, gain_dip(theta, np.pi))
which plots the elevation angle (theta) and is similar to the elevation of the example. However, it has a line at 180 degrees angle that should not appear.
I would like to plot the azimuthal angle, phi, too, but when I try it gives error:
phi = np.arange(-np.pi, np.pi,0.01)
ax = plt.subplot(111, polar=True)
# set zero west
ax.set_theta_zero_location('W')
ax.set_theta_direction('clockwise')
plt.plot(phi, gain_dip(np.pi/2, phi))
ValueError: x and y must have same first dimension, but have shapes (629,) and (1,)
And, it is possible to plot both angles like in the first plot?
I have a collection of 3D points. These points are sampled at constant levels (z=0,1,...,7). An image should make it clear:
These points are in a numpy ndarray of shape (N, 3) called X. The above plot is created using:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
X = load('points.npy')
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_wireframe(X[:,0], X[:,1], X[:,2])
ax.scatter(X[:,0], X[:,1], X[:,2])
plt.draw()
I'd like to instead triangulate only the surface of this object, and plot the surface. I do not want the convex hull of this object, however, because this loses subtle shape information I'd like to be able to inspect.
I have tried ax.plot_trisurf(X[:,0], X[:,1], X[:,2]), but this results in the following mess:
Any help?
Example data
Here's a snippet to generate 3D data that is representative of the problem:
import numpy as np
X = []
for i in range(8):
t = np.linspace(0,2*np.pi,np.random.randint(30,50))
for j in range(t.shape[0]):
# random circular objects...
X.append([
(-0.05*(i-3.5)**2+1)*np.cos(t[j])+0.1*np.random.rand()-0.05,
(-0.05*(i-3.5)**2+1)*np.sin(t[j])+0.1*np.random.rand()-0.05,
i
])
X = np.array(X)
Example data from original image
Here's a pastebin to the original data:
http://pastebin.com/YBZhJcsV
Here are the slices along constant z:
update 3
Here's a concrete example of what I describe in update 2. If you don't have mayavi for visualization, I suggest installing it via edm using edm install mayavi pyqt matplotlib.
Toy 2D contours stacked in 3D
Contours -> 3D surface
Code to generate the figures
from matplotlib import path as mpath
from mayavi import mlab
import numpy as np
def make_star(amplitude=1.0, rotation=0.0):
""" Make a star shape
"""
t = np.linspace(0, 2*np.pi, 6) + rotation
star = np.zeros((12, 2))
star[::2] = np.c_[np.cos(t), np.sin(t)]
star[1::2] = 0.5*np.c_[np.cos(t + np.pi / 5), np.sin(t + np.pi / 5)]
return amplitude * star
def make_stars(n_stars=51, z_diff=0.05):
""" Make `2*n_stars-1` stars stacked in 3D
"""
amps = np.linspace(0.25, 1, n_stars)
amps = np.r_[amps, amps[:-1][::-1]]
rots = np.linspace(0, 2*np.pi, len(amps))
zamps = np.linspace
stars = []
for i, (amp, rot) in enumerate(zip(amps, rots)):
star = make_star(amplitude=amp, rotation=rot)
height = i*z_diff
z = np.full(len(star), height)
star3d = np.c_[star, z]
stars.append(star3d)
return stars
def polygon_to_boolean(points, xvals, yvals):
""" Convert `points` to a boolean indicator mask
over the specified domain
"""
x, y = np.meshgrid(xvals, yvals)
xy = np.c_[x.flatten(), y.flatten()]
mask = mpath.Path(points).contains_points(xy).reshape(x.shape)
return x, y, mask
def plot_contours(stars):
""" Plot a list of stars in 3D
"""
n = len(stars)
for i, star in enumerate(stars):
x, y, z = star.T
mlab.plot3d(*star.T)
#ax.plot3D(x, y, z, '-o', c=(0, 1-i/n, i/n))
#ax.set_xlim(-1, 1)
#ax.set_ylim(-1, 1)
mlab.show()
if __name__ == '__main__':
# Make and plot the 2D contours
stars3d = make_stars()
plot_contours(stars3d)
xvals = np.linspace(-1, 1, 101)
yvals = np.linspace(-1, 1, 101)
volume = np.dstack([
polygon_to_boolean(star[:,:2], xvals, yvals)[-1]
for star in stars3d
]).astype(float)
mlab.contour3d(volume, contours=[0.5])
mlab.show()
update 2
I now do this as follows:
I use the fact that the paths in each z-slice are closed and simple and use matplotlib.path to determine points inside and outside of the contour. Using this idea, I convert the contours in each slice to a boolean-valued image, which is combined into a boolean-valued volume.
Next, I use skimage's marching_cubes method to obtain a triangulation of the surface for visualization.
Here's an example of the method. I think the data is slightly different, but you can definitely see that the results are much cleaner, and can handle surfaces that are disconnected or have holes.
Original answer
Ok, here's the solution I came up with. It depends heavily on my data being roughly spherical and sampled at uniformly in z I think. Some of the other comments provide more information about more robust solutions. Since my data is roughly spherical I triangulate the azimuth and zenith angles from the spherical coordinate transform of my data points.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.tri as mtri
X = np.load('./mydatars.npy')
# My data points are strictly positive. This doesn't work if I don't center about the origin.
X -= X.mean(axis=0)
rad = np.linalg.norm(X, axis=1)
zen = np.arccos(X[:,-1] / rad)
azi = np.arctan2(X[:,1], X[:,0])
tris = mtri.Triangulation(zen, azi)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(X[:,0], X[:,1], X[:,2], triangles=tris.triangles, cmap=plt.cm.bone)
plt.show()
Using the sample data from the pastebin above, this yields:
I realise that you mentioned in your question that you didn't want to use the convex hull because you might lose some shape information. I have a simple solution that works pretty well for your 'jittered spherical' example data, although it does use scipy.spatial.ConvexHull. I thought I would share it here anyway, just in case it's useful for others:
from matplotlib.tri import triangulation
from scipy.spatial import ConvexHull
# compute the convex hull of the points
cvx = ConvexHull(X)
x, y, z = X.T
# cvx.simplices contains an (nfacets, 3) array specifying the indices of
# the vertices for each simplical facet
tri = Triangulation(x, y, triangles=cvx.simplices)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.hold(True)
ax.plot_trisurf(tri, z)
ax.plot_wireframe(x, y, z, color='r')
ax.scatter(x, y, z, color='r')
plt.draw()
It does pretty well in this case, since your example data ends up lying on a more-or-less convex surface. Perhaps you could make some more challenging example data? A toroidal surface would be a good test case which the convex hull method would obviously fail.
Mapping an arbitrary 3D surface from a point cloud is a really tough problem. Here's a related question containing some links that might be helpful.
I am trying to make a contour plot in python with complex numbers (i am using matplotlib, pylab).
I am working with sharp bounds on harmonic polynomials, but specifically right now I am trying to plot:
Re(z(bar) - e^(z))= 0
Im(z(bar) - e^z) = 0
and plot them over each other in a contour in order to find their zeros to determine how many solutions there are to the equation z(bar) = e^(z).
Does anyone have experience in contour plotting, specifically with complex numbers?
import numpy as np
from matplotlib import pyplot as plt
x = np.r_[0:10:30j]
y = np.r_[0:10:20j]
X, Y = np.meshgrid(x, y)
Z = X*np.exp(1j*Y) # some arbitrary complex data
def plotit(z, title):
plt.figure()
cs = plt.contour(X,Y,z) # contour() accepts complex values
plt.clabel(cs, inline=1, fontsize=10) # add labels to contours
plt.title(title)
plt.savefig(title+'.png')
plotit(Z, 'real')
plotit(Z.real, 'explicit real')
plotit(Z.imag, 'imaginary')
plt.show()
EDIT: Above is my code, and note that for Z, I need to plot both real and imaginary parts of (x- iy) - e^(x+iy)=0. The current Z that is there is simply arbitrary. It is giving me an error for not having a 2D array when I try to plug mine in.
I don't know how you are plotting since you didn't post any code, but in general I advise moving away from using pylab or the pyplot interface to matplotlib, using the direct object methods is much more robust and just as simple. Here is an example of plotting contours of two sets of data on the same plot.
import numpy as np
import matplotlib.pyplot as plt
# making fake data
x = np.linspace(0, 2)
y = np.linspace(0, 2)
c = x[:,np.newaxis] * y
c2 = np.flipud(c)
# plot
fig, ax = plt.subplots(1, 1)
cont1 = ax.contour(x, y, c, colors='b')
cont2 = ax.contour(x, y, c2, colors='r')
cont1.clabel()
cont2.clabel()
plt.show()
For tom10, here is the plot this code produces. Note that setting colors to a single color makes distinguishing the two plots much easier.
I want to generate a heat map with my 3D data.
I have been able to plot trisurf using this data.
Can some one help me generate a heat map? I saw the online tutorials but they all seem quite complex for 3D. I found one on this website 'generating heatmap with scatter point in matplotlib but that is having only 2D data.
My code to generate trisurf is
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
n_angles = 36
n_radii = 8
# An array of radii
# Does not include radius r=0, this is to eliminate duplicate points
radii = np.linspace(0.125, 1.0, n_radii)
# An array of angles
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
# Repeat all angles for each radius
angles = np.repeat(angles[...,np.newaxis], n_radii, axis=1)
# Convert polar (radii, angles) coords to cartesian (x, y) coords
# (0, 0) is added here. There are no duplicate points in the (x, y) plane
x,y,z =np.loadtxt('output/flash_KR_endowment_duration_3D.dat',delimiter='\t',usecols=(0,1,2),unpack=True)
#x,y,z =np.loadtxt('output/disk_KR_endowment_duration_3D.dat',delimiter='\t',usecols=(0,1,2),unpack=True)
fig = plt.figure()
ax = fig.gca(projection='3d')
#fig.suptitle(suptitle, fontsize=12, fontweight='bold')
#ax.set_title("Disk Kryder's Rate-Endowment-Duration Plot",fontsize=12)
ax.set_title("Flash Kryder's Rate-Endowment-Duration Plot",fontsize=12)
ax.set_xlabel("Kryder's rate")
ax.set_ylabel("Duration")
ax.set_zlabel("Endowment")
surf = ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.2)
fig.colorbar(surf, shrink=.7, aspect=20)
plt.show()
Data is 3 column. say X,Y,Z. I have tried 3D scatter plot with color. But I am looking for heatmap.
If you only "want to use 3rd dimension for coloring", you can do it like this:
import pandas as pd
import numpy as np
import plotly.plotly as plotly
from plotly.graph_objs import Data, Heatmap
plotly.sign_in("username", "api_key") # this is annoying but you can get one after registering - free
# generate tridimentional data
pp = pd.Panel(np.random.rand(20, 20, 20))
# crunch (sum, average...) data along one axis
crunch = pp.sum(axis=0)
# now plot with plot.ly or matplotlib as you wish
data = Data([Heatmap(z=np.array(crunch))])
plotly.image.save_as(data, "filename.pdf")
Result - heatmap with 3rd variable of 3D data as colour:
Additionally, you can plot for each combination of axis with a loop:
## Plot
# for each axis, sum data along axis, plot heatmap
# dict is axis:[x,y,z], where z is a count of that variable
desc = {0 : ["ax1", "ax2", "ax3"], 1 : ["ax1", "ax2", "ax3"], 2 : ["ax1", "ax2", "ax3"]}
for axis in xrange(3):
# crunch (sum) data along one axis
crunch = pp.sum(axis=axis)
# now let's plot
data = Data([Heatmap(
z=np.array(crunch),
x=crunch.columns,
y=crunch.index)]
)
plotly.image.save_as(data,
"heatmap_{0}_vs_{1}_count_of_{2}".format(desc[axis][0], desc[axis][1], desc[axis][2])
)