Colouring the surface of a tetrahedra in matplotlib - python

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

Related

Annotating heatmap in matplotlib

I have to do three plots (contour, 3d surface, and heatmap) in matplotlib. The corresponding grid dimension for the three plots are ([0, 0], [0, 1], and [1, 0:1])
I have a few problems
The text annotation for heatmap (ax3), seem to fly out of ax3, into
ax1 and ax2. How can I constrain them to be within the ax3 only ?
Is this the fastest way to annotate text assuming that I do not want
to use seaborn ?
Can I get some tips on how to resolve my problems ?
Below is the code snippet to perform the plot operation
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gspec
from scipy.interpolate import griddata
import pyautogui
from scipy import stats
x = pyautogui.size()
width = x.width
height = x.height
x = np.arange(0, 10, 0.5)
y = np.arange(0, 10, 0.5)
X, Y = np.meshgrid(x, y)
data = 2 * (np.sin(X) + np.sin(3 * Y))
fig = plt.figure()
fig.set_figheight(height / 100)
fig.set_figwidth(width / 100)
fig.set_dpi(100)
gs = gspec.GridSpec(nrows=2, ncols=2)
ax1 = plt.subplot(gs[0, 0])
ax2 = plt.subplot(gs[0, 1], projection='3d')
ax3 = plt.subplot(gs[1, 0:1])
ctr = ax1.contourf(X, Y, data, 10, cmap='viridis')
ax1.clabel(ctr, inline=True, fontsize=8)
cbar = plt.colorbar(ctr, ax=ax1)
cbar.set_label('ColorbarLabel', size=15)
surf = ax2.plot_surface(X, Y, data, cmap='jet')
cbar1 = plt.colorbar(surf, ax=ax2)
cbar1.set_label('Colorbar2', size=15)
hmap = ax3.pcolormesh(X, Y, data, cmap='viridis')
cbar2 = plt.colorbar(hmap, ax=ax3)
for y in range(data.shape[0]):
for x in range(data.shape[1]):
ax3.text(x, y, '%.1f' % data[y, x], size=3)
I assume you want your heatmap to cover both columns. To achieve that you have to use ax3 = plt.subplot(gs[1, 0:2]): this tells matplotlib to use columns 0 and 1 (2 is excluded).
The text annotation for heatmap (ax3), seem to fly out of ax3, into ax1 and ax2. How can I constrain them to be within the ax3 only ?
That's because you are using the wrong coordinates in ax3.text.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gspec
from scipy.interpolate import griddata
import pyautogui
from scipy import stats
x = pyautogui.size()
width = x.width
height = x.height
x = np.arange(0, 10, 0.5)
y = np.arange(0, 10, 0.5)
X, Y = np.meshgrid(x, y)
data = 2 * (np.sin(X) + np.sin(3 * Y))
fig = plt.figure()
fig.set_figheight(height / 100)
fig.set_figwidth(width / 100)
fig.set_dpi(100)
gs = gspec.GridSpec(nrows=2, ncols=2)
ax1 = plt.subplot(gs[0, 0])
ax2 = plt.subplot(gs[0, 1], projection='3d')
ax3 = plt.subplot(gs[1, 0:2])
ctr = ax1.contourf(X, Y, data, 10, cmap='viridis')
ax1.clabel(ctr, inline=True, fontsize=8)
cbar = plt.colorbar(ctr, ax=ax1)
cbar.set_label('ColorbarLabel', size=15)
surf = ax2.plot_surface(X, Y, data, cmap='jet')
cbar1 = plt.colorbar(surf, ax=ax2)
cbar1.set_label('Colorbar2', size=15)
hmap = ax3.pcolormesh(X, Y, data, cmap='viridis')
cbar2 = plt.colorbar(hmap, ax=ax3)
for i in range(data.shape[0]):
for j in range(data.shape[1]):
ax3.text(x[j], y[i], '%.1f' % data[i, j], size=5)

Good-looking sphere in Matplotlib

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()

Python matplotlib set background rectangluar size of legend for grid spec

How do I set the background box size of legends in matplotlib?
Consider the left graph row alignment in pythons matplotlib (code posted in the end). The legends have been moved to the right because otherwise it covers essential data. They align vertically. But I feel like the different horizontal widths of the legends are kind of edgy. Therefore I want to change them to all have the same background box as shown in the right graph.
Is there any way to achieve that?
Current output
Wanted output
Code to reproduce the shown example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
loc = "center left"
x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
fig = plt.figure(figsize=(5, 5), constrained_layout=True)
gs = GridSpec(3, 1, figure=fig)
ax = fig.add_subplot(gs[0,:])
ax.plot(x, np.tan(x), label=r"$\tan(x) = \frac{\sin(x)}{\cos(x)}$")
ax.plot(x, np.tan(2*x), label=r"$\tan(2x)$")
ax.plot(x, np.tan(3*x), label=r"$\tan(3x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5))
ax = fig.add_subplot(gs[1,:])
ax.plot(x, np.sin(x), label=r"$\sin(x)$")
ax.plot(x, np.sin(2*x), label=r"$\sin(2x)$")
ax.plot(x, np.sin(4*x), label=r"$\sin(4x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5))
ax = fig.add_subplot(gs[2,:])
ax.plot(x, np.sin(x) * np.cos(x), label=r"$\sin(x) \times \cos(x)$")
ax.plot(x, np.sin(2*x) * np.cos(2*x), label=r"$\sin(2x) \times \cos(2x)$")
ax.plot(x, np.sin(3*x) * np.cos(3*x), label=r"$\sin(3x) \times \cos(3x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5))
plt.show()
As mentioned in the comments by #JohanC and #tmdavision, one can use the bbox_to_anchor=(x0, y0, width, height) together with mode='expand' to create the desired output. The following code shows the modifications.
Note that it is necessary to change the grid spec since it does not resize properly otherwise.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
loc = "center left"
x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
fig = plt.figure(figsize=(5, 5), constrained_layout=True)
gs = GridSpec(3, 5, figure=fig)
ax = fig.add_subplot(gs[0,:3])
ax.plot(x, np.tan(x), label=r"$\tan(x) = \frac{\sin(x)}{\cos(x)}$")
ax.plot(x, np.tan(2*x), label=r"$\tan(2x)$")
ax.plot(x, np.tan(3*x), label=r"$\tan(3x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5, 0.7, 0.1), mode="expand")
ax = fig.add_subplot(gs[1,:3])
ax.plot(x, np.sin(x), label=r"$\sin(x)$")
ax.plot(x, np.sin(2*x), label=r"$\sin(2x)$")
ax.plot(x, np.sin(4*x), label=r"$\sin(4x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5, 0.7, 0.1), mode="expand")
ax = fig.add_subplot(gs[2,:3])
ax.plot(x, np.sin(x) * np.cos(x), label=r"$\sin(x) \times \cos(x)$")
ax.plot(x, np.sin(2*x) * np.cos(2*x), label=r"$\sin(2x) \times \cos(2x)$")
ax.plot(x, np.sin(3*x) * np.cos(3*x), label=r"$\sin(3x) \times \cos(3x)$")
ax.legend(loc=loc, bbox_to_anchor=(1.02, 0.5, 0.7, 0.1), mode="expand")
plt.show()

Multiple 2D contour plots in one 3D figure in python

Is there any way available in python to plot multiple 2D contour plots in one 3D plot in python. I am currently using matplotlib for contouring, but not finding any option for what I am searching for. A sample image I have added. But I want to do it on Z-axis.
You can try this.
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
import matplotlib.patches as patches
fig = plt.figure()
ax = fig.gca(projection='3d')
x = np.linspace(0, 1, 100)
X, Y = np.meshgrid(x, x)
levels = np.linspace(-0.1, 0.4, 100) #(z_min,z_max,number of contour),
a=0
b=1
c=2
Z1 = a+.1*np.sin(2*X)*np.sin(4*Y)
Z2 = b+.1*np.sin(3*X)*np.sin(4*Y)
Z3 = c+.1*np.sin(4*X)*np.sin(5*Y)
plt.contourf(X, Y,Z1, levels=a+levels,cmap=plt.get_cmap('rainbow'))
plt.contourf(X, Y,Z2, levels=b+levels,cmap=plt.get_cmap('rainbow'))
plt.contourf(X, Y,Z3, levels=c+levels,cmap=plt.get_cmap('rainbow'))
ax.set_xlim3d(0, 1)
ax.set_ylim3d(0, 1)
ax.set_zlim3d(0, 2)
plt.show()
In order to plot true 2-D contour plots in one 3D plot, try this:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
x = np.linspace(0, 1, 100)
X, Y = np.meshgrid(x, x)
Z1 = .1*np.sin(2*X)*np.sin(4*Y)
Z2 = .1*np.sin(3*X)*np.sin(4*Y)
Z3 = .1*np.sin(4*X)*np.sin(5*Y)
levels=np.linspace(Z1.min(), Z1.max(), 100)
ax.contourf(X, Y,Z1, levels=levels, zdir='z', offset=0, cmap=plt.get_cmap('rainbow'))
levels=np.linspace(Z2.min(), Z2.max(), 100)
ax.contourf(X, Y,Z2, levels=levels, zdir='z', offset=1, cmap=plt.get_cmap('rainbow'))
levels=np.linspace(Z3.min(), Z3.max(), 100)
ax.contourf(X, Y,Z3, levels=levels, zdir='z', offset=2, cmap=plt.get_cmap('rainbow'))
ax.set_xlim3d(0, 1)
ax.set_ylim3d(0, 1)
ax.set_zlim3d(0, 2)
plt.show()
enter image description here

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