Good-looking sphere in Matplotlib - python

I've been trying to plot a (3d) sphere with some curves on it using Matplotlib, but so far the my results are disappointing.
I've tried with several RGB colors, opacities and colormaps, but the output is similar.
How could I do something like this Bloch Sphere? That's just what I'm looking for.
Thanks in advance!

To get a 3d plot more similar to the one you're showing, you can add some circular curves and lines along each axis. For example:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Make data
r = 10
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = r * np.outer(np.cos(u), np.sin(v))
y = r * np.outer(np.sin(u), np.sin(v))
z = r * np.outer(np.ones(np.size(u)), np.cos(v))
# Plot the surface
ax.plot_surface(x, y, z, color='linen', alpha=0.5)
# plot circular curves over the surface
theta = np.linspace(0, 2 * np.pi, 100)
z = np.zeros(100)
x = r * np.sin(theta)
y = r * np.cos(theta)
ax.plot(x, y, z, color='black', alpha=0.75)
ax.plot(z, x, y, color='black', alpha=0.75)
## add axis lines
zeros = np.zeros(1000)
line = np.linspace(-10,10,1000)
ax.plot(line, zeros, zeros, color='black', alpha=0.75)
ax.plot(zeros, line, zeros, color='black', alpha=0.75)
ax.plot(zeros, zeros, line, color='black', alpha=0.75)
plt.show()

Related

Colouring the surface of a tetrahedra in matplotlib

I'm trying to colour the faces of the tetrahedra determined by the edges shown here in green:
The plot is generated using this code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
%matplotlib notebook
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x,y,z = nCompoundTetra(bloch_vectors,nTetrahedron).getEdges()
x,y,z = list(x)[0],list(y)[0],list(z)[0]
ax.scatter3D(x, y, z, color = "black", s = 3.5)
ax.plot(x,y,z, color="g", linewidth= 1)
ax.set_xlim([-1.1, 1.1])
ax.set_ylim([-1.1, 1.1])
ax.set_zlim([-1.1, 1.1])
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
xs = np.outer(np.cos(u), np.sin(v))
ys = np.outer(np.sin(u), np.sin(v))
zs = np.outer(np.ones(np.size(u)), np.cos(v))
ax.plot_wireframe(xs, ys, zs, color="grey", alpha=0.15)
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([5, 5, 7, 7]))
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
ax.set_axis_off()
plt.tight_layout()
plt.show()
nCompoundTetra(bloch_vectors,nTetrahedron).getEdges() is a method I'm implementing to generate more tetrahedron

not all scatter points are shown on plot_surface

I am trying to visualize points on 3d surface.
For some reason only some points are shown in the plot.
When I start to move the plot around, some points suddenly appear. Is there a way to visualize points on a 3d surface?
This is the result I am getting right now:
And below the code used to generate it:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-5, 5, 0.1)
Y = np.arange(-5, 5, 0.1)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X ** 2 + Y ** 2)
Z = np.sin(R)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, antialiased=False)
X = np.arange(-5, 5, 1)
Y = np.arange(-5, 5, 1)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X ** 2 + Y ** 2)
Z = np.sin(R)
ax.scatter(X, Y, Z, c='r', marker='o')
# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()

Overlaying two plots using pcolor

I want to plot two numpy arrays Z1 and Z2 in the same figure, Z2 on top of Z1. The array Z2 contains only 0's and 1's, and I want 0's to be fully transparent (alpha = 0) and 1's transparent with some alpha > 0.
Here's the code and the resulting image:
import numpy as np
import matplotlib.pyplot as plt
N = 10
x = np.arange(0, N)
y = np.arange(0, N)
Z1 = np.random.rand(N,N)
Z2 = np.ones((N, N))
Z2[0:N//2, 0:N] = 0
X, Y = np.meshgrid(x, y)
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(8, 6))
plt.pcolormesh(X, Y, Z1, cmap=plt.cm.Blues)
plt.colorbar()
plt.pcolormesh(X, Y, Z2, cmap=plt.cm.Reds_r, alpha=0.3)
ax.set_xlabel(r'$x$', fontsize=22)
ax.set_ylabel(r'$y$', fontsize=22)
plt.show()
There are two problems:
Appearance of the unwanted grid lines
The 0's of Z2 are not fully transparent as needed
To get rid of the grid lines we can use imshow instead of pcolor, but I really want to use the values of x and y.
The easiest option to get some of the pixels transparent is to not draw them at all. This would be done by setting them to NaN (not a number).
import numpy as np
import matplotlib.pyplot as plt
N = 10
x = np.arange(0, N)
y = np.arange(0, N)
Z1 = np.random.rand(N,N)
Z2 = np.ones((N, N))
Z2[0:N//2, 0:N] = np.nan
X, Y = np.meshgrid(x, y)
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(8, 6))
plt.pcolormesh(X, Y, Z1, cmap=plt.cm.Blues)
plt.colorbar()
plt.pcolormesh(X, Y, Z2, cmap=plt.cm.Reds, vmin=0,vmax=1,alpha=0.3)
ax.set_xlabel(r'$x$', fontsize=22)
ax.set_ylabel(r'$y$', fontsize=22)
plt.show()

Python legend in 3dplot

I am plotting a 3d plot in python 2.7
When I try to plot a 3d plot with color and marker as in 2D plot() function. I come across an error.
So I tried to plot line separately and measured points with markers separately using scatter() function.
When I create legend entries my legend looks like this
But I don't want to have duplicate legend entries instead
I want my legend entries to group with colour, or
Is it possible have both marker and line as a single entry so that there are only 5 entries in my legend
I found a similar question to this (How to make custom legend in matplotlib) but it does not solve my problem
I am appending a code similar to my problem
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
ax.plot(x, y, z, label='parametric curve 1')
ax.scatter(x, y, z, label='parametric curve 1',marker = 'o')
x = r * np.sin(theta + 1)
y = r * np.cos(theta + 1)
ax.plot(x, y, z, label='parametric curve 2')
ax.scatter(x, y, z, label='parametric curve 2',marker = 'o')
ax.legend()
plt.show()
The above code gives me a plot shown below
Plot
But I want my legend to have only two entries
Are you using the standard Matplotlib library to generate these 3D plots? If so, starting from the example in the documentation (http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#line-plots) it seems to work fine:
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
ax.plot(x, y, z, label='parametric curve 1', marker='o')
x = r * np.sin(theta + 1)
y = r * np.cos(theta + 1)
ax.plot(x, y, z, label='parametric curve 2', marker='o')
ax.legend()
plt.show()

Add cylinder to plot

I would like to add a transparent cylinder to my 3D scatter plot. How can I do it?
This is the code I am using to make the plot:
fig = plt.figure(2, figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X, Y, Z, c=Z,cmap=plt.cm.Paired)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
plt.xticks()
Today I have to do the same thing in my project about adding a transparent cylinder in the result. This is the code I get finally. So I share it with you guys just for learning
import numpy as np
def data_for_cylinder_along_z(center_x,center_y,radius,height_z):
z = np.linspace(0, height_z, 50)
theta = np.linspace(0, 2*np.pi, 50)
theta_grid, z_grid=np.meshgrid(theta, z)
x_grid = radius*np.cos(theta_grid) + center_x
y_grid = radius*np.sin(theta_grid) + center_y
return x_grid,y_grid,z_grid
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
Xc,Yc,Zc = data_for_cylinder_along_z(0.2,0.2,0.05,0.1)
ax.plot_surface(Xc, Yc, Zc, alpha=0.5)
plt.show()
And you will get this beautiful figure.
One possible method is to use the plot_surface. Adapting the solution given in this blog post then have
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Scatter graph
N = 100
X = np.random.uniform(-1, 1, N)
Y = np.random.uniform(-1, 1, N)
Z = np.random.uniform(-2, 2, N)
ax.scatter(X, Y, Z)
# Cylinder
x=np.linspace(-1, 1, 100)
z=np.linspace(-2, 2, 100)
Xc, Zc=np.meshgrid(x, z)
Yc = np.sqrt(1-Xc**2)
# Draw parameters
rstride = 20
cstride = 10
ax.plot_surface(Xc, Yc, Zc, alpha=0.2, rstride=rstride, cstride=cstride)
ax.plot_surface(Xc, -Yc, Zc, alpha=0.2, rstride=rstride, cstride=cstride)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
plt.show()
I've added some minimal configuration of the surface, better can be achieved by consulting the docs.
I improved on #Greg's answer and made a solid 3D cylinder with a top and bottom surface and rewrote the equation so that you can translate in the x, y,and z
from mpl_toolkits.mplot3d import Axes3D
import mpl_toolkits.mplot3d.art3d as art3d
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Circle
def plot_3D_cylinder(radius, height, elevation=0, resolution=100, color='r', x_center = 0, y_center = 0):
fig=plt.figure()
ax = Axes3D(fig, azim=30, elev=30)
x = np.linspace(x_center-radius, x_center+radius, resolution)
z = np.linspace(elevation, elevation+height, resolution)
X, Z = np.meshgrid(x, z)
Y = np.sqrt(radius**2 - (X - x_center)**2) + y_center # Pythagorean theorem
ax.plot_surface(X, Y, Z, linewidth=0, color=color)
ax.plot_surface(X, (2*y_center-Y), Z, linewidth=0, color=color)
floor = Circle((x_center, y_center), radius, color=color)
ax.add_patch(floor)
art3d.pathpatch_2d_to_3d(floor, z=elevation, zdir="z")
ceiling = Circle((x_center, y_center), radius, color=color)
ax.add_patch(ceiling)
art3d.pathpatch_2d_to_3d(ceiling, z=elevation+height, zdir="z")
ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_zlabel('z-axis')
plt.show()
# params
radius = 3
height = 10
elevation = -5
resolution = 100
color = 'r'
x_center = 3
y_center = -2
plot_3D_cylinder(radius, height, elevation=elevation, resolution=resolution, color=color, x_center=x_center, y_center=y_center)

Categories