在曲面图上画线

Zki*_*llt 9 python plot matplotlib matplotlib-3d

我希望能够看到 3D 表面上的线(和点)位于表面顶部(第二张图像),而不是后面(第一张图像)。这是我的 3D 函数:

def f(x, y): 
    return np.sin(2*x) * np.cos(2*y)
Run Code Online (Sandbox Code Playgroud)

3D 表面的 X、Y、Z:

x = np.linspace(-2, 2, 100)
y = np.linspace(-2, 2, 100)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
Run Code Online (Sandbox Code Playgroud)

我生成了 x 点 (xx) 和 y 点 (yy) 的向量,其中 zz = f(xx,yy)

fig = plt.figure(figsize=(8,6))
ax = plt.axes(projection='3d')
ax.scatter(xx, yy, zz, c='r', marker='o')
#ax.plot(xx,yy,zz, c= 'r')

ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                cmap='viridis', edgecolor='none')
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

正如你所看到的,点在图后面,图形覆盖了点。我想看看情节上的要点。我应该怎么办?

我希望能够看到像这样的点和线:

在此输入图像描述

编辑:这是我生成点的方式:

for i in range(1,2000):
    [a, b] =  np.random.rand(2,1)*np.sign(np.random.randn(2,1))*2
    xx = np.append(xx, a)
    yy = np.append(yy, b)
Run Code Online (Sandbox Code Playgroud)

我注意到,如果我写下来,zz = f(xx,yy) + epsilon我就能看到要点。如果epsilon = 0,那么从数学上讲,这些点位于表面上,并且我无法清楚地看到它们,就像第一张图片中一样。如果epsilon > 0.05,我可以看到这些点,但这意味着将点向上移动。我真的不喜欢这个解决方案。如果一个点在一个曲面上,则该曲面具有优先权,她的曲面看起来位于该点之上。我希望我的图形是相反的。

And*_*eak 9

首先我要说的是,你想要的东西有些不明确。您希望以始终在图中显示的方式(即在表面上方)精确地在基础表面上绘制点,而不需要明确地将它们向上移动。这已经是一个问题了,因为

  1. 浮点运算意味着点和曲面的精确坐标可能会根据机器精度的数量级而变化,因此尝试依赖精确的等式是行不通的。
  2. 即使数字精确到无限精度,表面也是用一组近似平面绘制的。这意味着您的确切数据点将位于函数凸的近似曲面下方。

然而,最大的问题是,在绘制图形中的多个或复杂对象时, matplotlib 中的 3D 绘图并不可靠。特别是,渲染器本质上是 2d 的,在尝试找出对象的相对表观位置时经常会遇到问题。为了克服这个问题,可以尝试解决这个问题,或者切换到mayavi使用适当的 3D 渲染器之类的东西。

不幸的是,zorder可选的关键字参数通常被 3d 轴对象忽略。所以我在 pyplot 中能想到的唯一的东西就是你几乎拥有的,注释掉的:使用ax.plot而不是ax.scatter。虽然后者产生第一个图中显示的图(每个散点由于某种原因隐藏,无论视角如何),但前者导致第二个图中显示的图(其中点可见)。通过从绘图样式中删除线条,我们几乎得到了你想要的:

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

def f(x, y):                        
    return np.sin(2*x) * np.cos(2*y)

# data for the surface
x = np.linspace(-2, 2, 100)
X, Y = np.meshgrid(x, x)
Z = f(X, Y)

# data for the scatter
xx = 4*np.random.rand(1000) - 2
yy = 4*np.random.rand(1000) - 2
zz = f(xx,yy)

fig = plt.figure(figsize=(8,6))
ax = plt.axes(projection='3d')
#ax.scatter(xx, yy, zz, c='r', marker='o')
ax.plot(xx, yy, zz, 'ro', alpha=0.5) # note the 'ro' (no '-') and the alpha

ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                cmap='viridis', edgecolor='none')
Run Code Online (Sandbox Code Playgroud)

matplotlib 冲浪与绘图

但不完全是:很快就会发现,在这种情况下,点总是可见的,即使它们应该隐藏在表面的一部分后面:

# note the change in points: generate only in the "back" quadrant
xx = 2*np.random.rand(1000) - 2
yy = 2*np.random.rand(1000)
zz = f(xx,yy)

fig = plt.figure(figsize=(8,6))
ax = plt.axes(projection='3d')
ax.plot(xx,yy,zz, 'ro', alpha=0.5)

ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                cmap='viridis', edgecolor='none')
Run Code Online (Sandbox Code Playgroud)

matplotlib surf 与 scatter 仅在后面有点,显示应隐藏的虚假可见点

很容易看出,前面的凹凸应该隐藏了背景中的一大块点,但是这些点是可见的。这正是 pyplot 在复杂 3D 可视化中遇到的问题。因此,我的底线是,你无法使用 matplotlib 可靠地完成你想要的事情。不管怎样,我不确定这样的情节是否容易理解。


为了以更积极的方式结束,您可以使用以下方法来完成此操作mayavi(为此您需要安装vtk最好通过包管理器完成):

import numpy as np
from mayavi import mlab
from matplotlib.cm import get_cmap # for viridis

def f(x, y):
    return np.sin(2*x) * np.cos(2*y)

# data for the surface
x = np.linspace(-2, 2, 100)
X, Y = np.meshgrid(x, x)
Z = f(X, Y)

# data for the scatter
xx = 4*np.random.rand(1000) - 2
yy = 4*np.random.rand(1000) - 2
zz = f(xx,yy)

fig = mlab.figure(bgcolor=(1,1,1))
# note the transpose in surf due to different conventions compared to meshgrid
su = mlab.surf(X.T, Y.T, Z.T)
sc = mlab.points3d(xx, yy, zz, scale_factor=0.1, scale_mode='none',
                   opacity=1.0, resolution=20, color=(1,0,0))

# manually set viridis for the surface
cmap_name = 'viridis'
cdat = np.array(get_cmap(cmap_name,256).colors)
cdat = (cdat*255).astype(int)
su.module_manager.scalar_lut_manager.lut.table = cdat

mlab.show()
Run Code Online (Sandbox Code Playgroud)

mayavi 示例:以小球体为点的表面

正如您所看到的,结果是一个交互式 3D 图,其中表面上的数据点是适当的球体。人们可以调整不透明度和球体比例设置以获得令人满意的可视化效果。由于正确的 3D 渲染,无论视角如何,您都可以看到每个点的适当数量。