How to add dynamic contour plot - python

I wrote some code in juypter to visualize the Bivariate normal distribution. I want to modify the code so that I can visualize the contour plot(isodensity, say the x-y surface) at the same time. What should I add?
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib as mpl
%matplotlib
if __name__ == '__main__':
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
d = np.random.randn(10000000, 2)
N = 30
density, edges = np.histogramdd(d, bins=[30, 30])
print("样本总数: ", np.sum(density))
density = density/density.max()
x = y = np.arange(N)
t = np.meshgrid(x,y)
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(t[0], t[1], density, c='r', s=15*density, marker='o', depthshade=True)
ax.plot_surface(t[0], t[1], density, cmap='rainbow', rstride=1, cstride=1, alpha=0.9, lw=1)
cset = ax.contourf(x, y, density,
zdir ='z',
offset = np.min(density),
)
ax.set_xlabel("x轴")
ax.set_ylabel("y轴")
ax.set_zlabel("z轴")
plt.title("二元高斯分布")
# plt.tight_layout(0.1)
plt.show()

Related

Plot 3d points (x,y,z) in 2d plot with colorbar

I have computed a lot (~5000) of 3d points (x,y,z) in a quite complicated way so I have no function such that z = f(x,y). I can plot the 3d surface using
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
surf = ax.plot_trisurf(X, Y, Z, cmap=cm.coolwarm, vmin=np.nanmin(Z), vmax=np.nanmax(Z))
I would like to plot this also in 2d, with a colorbar indicating the z-value. I know there is a simple solution using ax.contour if my z is a matrix, but here I only have a vector.
Attaching the plot_trisurf result when rotated to xy-plane. This is what I what like to achieve without having to rotate a 3d plot. In this, my variable surface_points is an np.array with size 5024 x 3.
I had the same problems in one of my codes, I solved it this way:
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pylab as plt
from matplotlib import cm
N = 10000
surface_points = np.random.rand(N,3)
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
nx = 10*int(np.sqrt(N))
xg = np.linspace(X.min(), X.max(), nx)
yg = np.linspace(Y.min(), Y.max(), nx)
xgrid, ygrid = np.meshgrid(xg, yg)
ctr_f = griddata((X, Y), Z, (xgrid, ygrid), method='linear')
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.contourf(xgrid, ygrid, ctr_f, cmap=cm.coolwarm)
plt.show()
You could use a scatter plot to display a projection of your z color onto the x-y axis.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
N = 10000
surface_points = np.random.rand(N,3)
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
# fig = plt.figure()
# ax = fig.add_subplot(projection='3d')
# surf = ax.plot_trisurf(X, Y, Z, cmap=cm.coolwarm, vmin=np.nanmin(Z), vmax=np.nanmax(Z))
fig = plt.figure()
cmap = cm.get_cmap('coolwarm')
color = cmap(Z)[..., :3]
plt.scatter(X,Y,c=color)
plt.show()
Since you seem to have a 3D shape that is hollow, you could split the projection into two like if you cur the shape in two pieces.
fig = plt.figure()
plt.subplot(121)
plt.scatter(X[Z<0.5],Y[Z<0.5],c=color[Z<0.5])
plt.title('down part')
plt.subplot(122)
plt.scatter(X[Z>=0.5],Y[Z>=0.5],c=color[Z>+0.5])
plt.title('top part')
plt.show()

How can I smooth ConvexHull like an ellipse?

I am trying to plot an encircling area in a scatter plot with Matplotlib, and I want to smooth a Convex Hull like an ellipse. Could anyone give me some suggestions?
Here is my code
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from matplotlib import patches
midwest = pd.read_csv('https://raw.githubusercontent.com/gitony0101/X4DS/main/data/midwest_filter.csv')
midwest_encircle_data = midwest.query("state=='IN'")
_, ax = plt.subplots(figsize=(10, 8))
g = sns.scatterplot(x='area',
y='poptotal',
data=midwest,
hue='state',
palette='tab10',
size='popdensity',
ax=ax)
def encircle(x, y, ax=None, **kw):
ax = ax or plt.gca()
p = np.stack([x, y], axis=1)
hull = ConvexHull(p)
poly = patches.Polygon(xy=p[hull.vertices, :], closed=True, **kw)
ax.add_patch(poly)
g.set(xlim=(0.0, 0.1),
ylim=(0, 90000),
xlabel='Area',
ylabel='Population',
title='Bubble Plot with Encircling')
x = midwest_encircle_data['area']
y = midwest_encircle_data['poptotal']
encircle(x, y, ec='k', fc='gold', alpha=0.1, ax=ax)
encircle(x, y, ec='firebrick', fc='none', linewidth=1.5, ax=ax)
plt.show()

Matplotlib 3d Plot Colorbar Scale

I have a 3d plot with a colorbar and I would like the colorbar's size to scale with the size of the projection, no matter the orientation I select with ax.view_init.
It would also be great if I could get the aspect ratio of the 3d plot to be equal at the same time as well.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.colors
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.view_init(elev=90, azim=0)
x = np.arange(3)
X,Y = np.meshgrid(x,x)
Z = np.ones_like(X)
V = np.array([[3,2,2],[1,0,3],[2,1,0]])
norm = matplotlib.colors.Normalize(vmin=0, vmax=3)
ax.plot_surface(X, Y, Z, facecolors=plt.cm.jet(norm(V)), shade=False)
m = cm.ScalarMappable(cmap=plt.cm.jet, norm=norm)
m.set_array([])
plt.colorbar(m)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()
Example code stolen shamelessly from this question

Adding legend to a surface plot

I am trying to add legend to a surface plot but unable to do so. Here is the code.
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import random
def fun(x, y):
return 0.063*x**2 + 0.0628*x*y - 0.15015876*x + 96.1659*y**2 - 74.05284306*y + 14.319143466051
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-1.0, 1.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array([fun(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.plot(color='red',label='Lyapunov function on XY plane',linewidth=4) # Adding legend
plt.show()
Kindly help. Thanks in advance.
It is not trivial to make a legend in a 3D axis. You can use the following hack:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib as mpl
import random
def fun(x, y):
return 0.063*x**2 + 0.0628*x*y - 0.15015876*x + 96.1659*y**2 - 74.05284306*y + 14.319143466051
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-1.0, 1.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array([fun(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fake2Dline = mpl.lines.Line2D([0],[0], linestyle="none", c='b', marker = 'o')
ax.legend([fake2Dline], ['Lyapunov function on XY plane'], numpoints = 1)
plt.show()
I would say a title is more appropriate than a legend in this case.
According to this question, the issue is ongoing, and there is a relatively simple workaround. You can manually set the two missing attributes that would allow legend to automatically create the patch for you:
surf = ax.plot_surface(X, Y, Z, label='Lyapunov function on XY plane')
surf._edgecolors2d = surf._edgecolor3d
surf._facecolors2d = surf._facecolor3d
ax.legend()
The attribute names on the right hand side of the assignment are surf._edgecolors3d and surf.facecolors3d for matplotlib < v3.3.3.

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