通过三维表面绘制二维平面

8

我正在尝试使用Numpy和Matplotlib可视化一个二维平面通过一个三维图形,以解释偏导数的直观感受。

具体而言,我使用的函数是J(θ1,θ2) = θ1^2 + θ2^2,并且我想在θ2=0处绘制一个θ1-J(θ1,θ2)平面。

我已经成功绘制了一个二维平面,但是二维平面和三维图形的重叠部分并不完全正确,并且二维平面略微偏离,因为我希望该平面看起来像在θ2=0处穿过了三维图形。

如果您能在此方面提供专业意见,那将是非常好的,谢谢。

    def f(theta1, theta2):
        return theta1**2 + theta2**2

    fig, ax = plt.subplots(figsize=(6, 6), 
                           subplot_kw={'projection': '3d'})

    x,z = np.meshgrid(np.linspace(-1,1,100), np.linspace(0,2,100))
    X = x.T
    Z = z.T
    Y = 0 * np.ones((100, 100))
    ax.plot_surface(X, Y, Z)

    r = np.linspace(-1,1,100)
    theta1_grid, theta2_grid = np.meshgrid(r,r)
    J_grid = f(theta1_grid, theta2_grid)
    ax.contour3D(theta1_grid,theta2_grid,J_grid,500,cmap='binary')

    ax.set_xlabel(r'$\theta_1$',fontsize='large')
    ax.set_ylabel(r'$\theta_2$',fontsize='large')
    ax.set_zlabel(r'$J(\theta_1,\theta_2)$',fontsize='large')
    ax.set_title(r'Fig.2 $J(\theta_1,\theta_2)=(\theta_1^2+\theta_2^2)$',fontsize='x-large')

    plt.tight_layout()
    plt.show()

这是代码输出的图像:

plot showing a parabolic surface with a vertical plane weirdly superimposed on it


对于2D平面的偏移:这只是看起来那样,它实际上定位正确。对于3D图形:这将是我的3D图形在某些视角下看起来不正确的常见情况之一 - ImportanceOfBeingErnest
我明白了,确实我看到过有关Matplotlib在3D绘图方面不是最佳选择的评论。我将探索一些其他选项,比如Plotly。谢谢 :) - Misa Ogura
1个回答

12

@ImportanceOfBeingErnest在评论中指出的那样,你的代码没问题,但是Matplotlib使用的是2D引擎,因此3D图很容易显示出奇怪的伪影。特别地,对象是一个接一个地呈现的,因此两个3D对象通常完全位于另一个3D对象的前面或后面,这使得使用Matplotlib可视化交织的3D对象变得几乎不可能。

我个人的替代建议是mayavi(具有惊人的灵活性和可视化效果,但学习曲线相对陡峭),但是我想展示一个技巧,用它可以彻底解决这个问题。思路是使用两个表面之间的隐形桥梁将您的两个独立对象变成单个对象。该方法的可能缺点是:

  1. 您需要将两个表面都绘制为表面而不是contour3D,并且
  2. 输出结果严重依赖透明度,因此您需要使用能够处理此功能的后端。

声明:我从现已废弃的Stack Overflow文档项目的Matplotlib主题的一位贡献者那里学到了这个技巧,但不幸的是我不记得那个用户是谁。

为了在您的用例中使用此技巧,我们实际上必须将contour3D调用转换为另一个plot_surface调用。我认为这总体而言并不太糟糕;如果您发现生成的图形具有太多面以供交互使用,则可能需要重新考虑切割平面的密度。我们还必须明确定义一个点对点的颜色映射,其中alpha通道贡献了两个表面之间的透明桥梁。由于我们需要将两个表面缝合在一起,因此至少一个表面的“平面内”维度必须匹配;在本例中,我确保y轴上的点相同。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def f(theta1, theta2):
    return theta1**2 + theta2**2

fig, ax = plt.subplots(figsize=(6, 6),
                       subplot_kw={'projection': '3d'})

# plane data: X, Y, Z, C (first three shaped (nx,ny), last one shaped (nx,ny,4))
x,z = np.meshgrid(np.linspace(-1,1,100), np.linspace(0,2,100)) # <-- you can probably reduce these sizes
X = x.T
Z = z.T
Y = 0 * np.ones((100, 100))
# colormap for the plane: need shape (nx,ny,4) for RGBA values
C = np.full(X.shape + (4,), [0,0,0.5,1]) # dark blue plane, fully opaque

# surface data: theta1_grid, theta2_grid, J_grid, CJ (shaped (nx',ny) or (nx',ny,4))
r = np.linspace(-1,1,X.shape[1]) # <-- we are going to stitch the surface along the y dimension, sizes have to match
theta1_grid, theta2_grid = np.meshgrid(r,r)
J_grid = f(theta1_grid, theta2_grid)
# colormap for the surface; scale data to between 0 and 1 for scaling
CJ = plt.get_cmap('binary')((J_grid - J_grid.min())/J_grid.ptp())

# construct a common dataset with an invisible bridge, shape (2,ny) or (2,ny,4)
X_bridge = np.vstack([X[-1,:],theta1_grid[0,:]])
Y_bridge = np.vstack([Y[-1,:],theta2_grid[0,:]])
Z_bridge = np.vstack([Z[-1,:],J_grid[0,:]])
C_bridge = np.full(Z_bridge.shape + (4,), [1,1,1,0]) # 0 opacity == transparent; probably needs a backend that supports transparency!

# join the datasets
X_surf = np.vstack([X,X_bridge,theta1_grid])
Y_surf = np.vstack([Y,Y_bridge,theta2_grid])
Z_surf = np.vstack([Z,Z_bridge,J_grid])
C_surf = np.vstack([C,C_bridge,CJ])

# plot the joint datasets as a single surface, pass colors explicitly, set strides to 1
ax.plot_surface(X_surf, Y_surf, Z_surf, facecolors=C_surf, rstride=1, cstride=1)

ax.set_xlabel(r'$\theta_1$',fontsize='large')
ax.set_ylabel(r'$\theta_2$',fontsize='large')
ax.set_zlabel(r'$J(\theta_1,\theta_2)$',fontsize='large')
ax.set_title(r'Fig.2 $J(\theta_1,\theta_2)=(\theta_1^2+\theta_2^2)$',fontsize='x-large')

plt.tight_layout()
plt.show()

两个角度的结果:

结果1,默认视图;一切都很好 结果2,仍然一切顺利

可以看到,结果相当不错。您可以开始调整表面的透明度,以查看是否可以使截面更加清晰可见。您还可以将桥的不透明度设置为1,以查看表面实际上是如何拼接在一起的。总之,我们所要做的就是使用您现有的数据,确保它们的尺寸匹配,并定义显式的颜色映射和表面之间的辅助桥梁。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接