通过 3D 表面绘制 2D 平面 [英] Plotting a 2D plane through a 3D surface

查看:32
本文介绍了通过 3D 表面绘制 2D 平面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Numpy 和 Matplotlib 将 2D 平面通过 3D 图形可视化,以解释偏导数的直觉.

具体来说,我使用的函数是 J(θ1,θ2) = θ1^2 + θ2^2,我想在 θ2=0 处绘制 θ1-J(θ1,θ2) 平面.

我已经设法用下面的代码绘制了一个 2D 平面,但是 2D 平面和 3D 图形的叠加不太正确,并且 2D 平面略微偏离,因为我希望平面看起来像是在切割θ2=0 时的 3D.

如果我能借用你这方面的专业知识就太好了,谢谢.

 def f(theta1, theta2):返回 theta1**2 + theta2**2图, ax = plt.subplots(figsize=(6, 6),subplot_kw={'投影':'3d'})x,z = np.meshgrid(np.linspace(-1,1,100), np.linspace(0,2,100))X = x.TZ = z.TY = 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'$	heta_1$',fontsize='large')ax.set_ylabel(r'$	heta_2$',fontsize='large')ax.set_zlabel(r'$J(	heta_1,	heta_2)$',fontsize='large')ax.set_title(r'Fig.2 $J(	heta_1,	heta_2)=(	heta_1^2+	heta_2^2)$',fontsize='x-large')plt.tight_layout()plt.show()

这是代码输出的图片:

解决方案

As

如您所见,结果相当不错.您可以开始处理表面的各个透明度,看看是否可以使该横截面更加可见.您还可以将桥的不透明度切换为 1,以查看您的曲面实际上是如何缝合在一起的.总而言之,我们要做的就是获取您现有的数据,确保它们的大小匹配,并定义明确的颜色图和表面之间的辅助桥梁.

I'm trying to visualise a 2D plane cutting through a 3D graph with Numpy and Matplotlib to explain the intuition of partial derivatives.

Specifically, the function I'm using is J(θ1,θ2) = θ1^2 + θ2^2, and I want to plot a θ1-J(θ1,θ2) plane at θ2=0.

I have managed to plot a 2D plane with the below code but the superposition of the 2D plane and the 3D graph isn't quite right and the 2D plane is slightly off, as I want the plane to look like it's cutting the 3D at θ2=0.

It would be great if I can borrow your expertise on this, thanks.

    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'$	heta_1$',fontsize='large')
    ax.set_ylabel(r'$	heta_2$',fontsize='large')
    ax.set_zlabel(r'$J(	heta_1,	heta_2)$',fontsize='large')
    ax.set_title(r'Fig.2 $J(	heta_1,	heta_2)=(	heta_1^2+	heta_2^2)$',fontsize='x-large')

    plt.tight_layout()
    plt.show()

This is the image output by the code:

解决方案

As @ImportanceOfBeingErnest noted in a comment, your code is fine but matplotlib has a 2d engine, so 3d plots easily show weird artifacts. In particular, objects are rendered one at a time, so two 3d objects are typically either fully in front of or fully behind one another, which makes the visualization of interlocking 3d objects near impossible using matplotlib.

My personal alternative suggestion would be mayavi (incredible flexibility and visualizations, pretty steep learning curve), however I would like to show a trick with which the problem can often be removed altogether. The idea is to turn your two independent objects into a single one using an invisible bridge between your surfaces. Possible downsides of the approach are that

  1. you need to plot both surfaces as surfaces rather than a contour3D, and
  2. the output relies heavily on transparency, so you need a backend that can handle that.

Disclaimer: I learned this trick from a contributor to the matplotlib topic of the now-defunct Stack Overflow Documentation project, but unfortunately I don't remember who that user was.

In order to use this trick for your use case, we essentially have to turn that contour3D call to another plot_surface one. I don't think this is overall that bad; you perhaps need to reconsider the density of your cutting plane if you see that the resulting figure has too many faces for interactive use. We also have to explicitly define a point-by-point colormap, the alpha channel of which contributes the transparent bridge between your two surfaces. Since we need to stitch the two surfaces together, at least one "in-plane" dimension of the surfaces have to match; in this case I made sure that the points along "y" are the same in the two cases.

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'$	heta_1$',fontsize='large')
ax.set_ylabel(r'$	heta_2$',fontsize='large')
ax.set_zlabel(r'$J(	heta_1,	heta_2)$',fontsize='large')
ax.set_title(r'Fig.2 $J(	heta_1,	heta_2)=(	heta_1^2+	heta_2^2)$',fontsize='x-large')

plt.tight_layout()
plt.show()

The result from two angles:

As you can see, the result is pretty decent. You can start playing around with the individual transparencies of your surfaces to see if you can make that cross-section more visible. You can also switch the opacity of the bridge to 1 to see how your surfaces are actually stitched together. All in all what we had to do was take your existing data, make sure their sizes match, and define explicit colormaps and the auxiliary bridge between the surfaces.

这篇关于通过 3D 表面绘制 2D 平面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆