在matplotlib中显示最大表面?

Nic*_*eet 18 matlab plot matplotlib surface

我正在使用matplotlib在同一个图上绘制多个曲面,我想只看到最顶层的曲面,就像matlab所示.

Matlab 3D视图: Matlab 3D视图

Matlab顶视图: Matlab顶视图

Matplotlib 3D视图: Matplotlib 3D视图

Matplotlib顶视图: Matplotlib顶视图

我怎样才能让Matplotlib显示类似于Matlab的结果,其中最顶层的类显示在顶部,而不是一个类优先于另一个?

wil*_*ill 10

在他们的回答中,我想到像mgab提到的一些肮脏的黑客,但后来决定走一条相当简单的路线:

你可以纯粹通过使用透明度获得类似的效果,你只需要确保透明度足够低,否则你仍然会发生明显重叠的事情:

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
from scipy.special import erf

fig = plt.figure()
ax = fig.gca(projection='3d')

X = np.arange(0, 6, 0.25)
Y = np.arange(0, 6, 0.25)
X, Y = np.meshgrid(X, Y)

Z1 = np.zeros_like(X)
Z2 = np.ones_like(X)

for i in range(len(X)):
  for j in range(len(X[0])):
    Z1[i,j] = 0.5*(erf((X[i,j]+Y[i,j]-4.5)*0.5)+1)
    Z2[i,j] = 0.5*(erf((-X[i,j]-Y[i,j]+4.5)*0.5)+1)


alpha = 0.25

surf1 = ax.plot_surface(X, Y, Z1, cstride=2, rstride=1, cmap=cm.Oranges, linewidth=0, antialiased=False, alpha=alpha)

surf2 = ax.plot_surface(X, Y, Z2, cstride=2, rstride=1, cmap=cm.Blues, linewidth=0, antialiased=False, alpha=alpha)

ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

fig.colorbar(surf1, shrink=0.5, aspect=5)
fig.colorbar(surf2, shrink=0.5, aspect=5)

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

在此输入图像描述

在此输入图像描述

添加一个交叉线将是一个很好的补充,我暂时没有一种简单的方法来添加它.

编辑:使用他的"桥"解决方案大量窃取mgab的答案,但随后也使用表面的颜色贴图,并使用RGBA元组设置桥面是透明的,你几乎可以得到你想要的:

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

fig = plt.figure()
ax = fig.gca(projection='3d')

X = np.arange(0, 6, 0.25)
Y = np.arange(0, 6, 0.25)
X, Y = np.meshgrid(X, Y)

Z1 = np.empty_like(X)
Z2 = np.empty_like(X)
C1 = np.empty_like(X, dtype=object)
C2 = np.empty_like(X, dtype=object)

for i in range(len(X)):
  for j in range(len(X[0])):
    z1 = 0.5*(erf((X[i,j]+Y[i,j]-4.5)*0.5)+1)
    z2 = 0.5*(erf((-X[i,j]-Y[i,j]+4.5)*0.5)+1)
    Z1[i,j] = z1
    Z2[i,j] = z2

    # If you want to grab a colour from a matplotlib cmap function, 
    # you need to give it a number between 0 and 1. z1 and z2 are 
    # already in this range, so it just works.
    C1[i,j] = plt.get_cmap("Oranges")(z1)
    C2[i,j] = plt.get_cmap("Blues")(z2)


# Create a transparent bridge region
X_bridge = np.vstack([X[-1,:],X[-1,:]])
Y_bridge = np.vstack([Y[-1,:],Y[-1,:]])
Z_bridge = np.vstack([Z1[-1,:],Z2[-1,:]])
color_bridge = np.empty_like(Z_bridge, dtype=object)

color_bridge.fill((1,1,1,0)) # RGBA colour, onlt the last component matters.

# Join the two surfaces flipping one of them (using also the bridge)
X_full = np.vstack([X, X_bridge, np.flipud(X)])
Y_full = np.vstack([Y, Y_bridge, np.flipud(Y)])
Z_full = np.vstack([Z1, Z_bridge, np.flipud(Z2)])
color_full = np.vstack([C1, color_bridge, np.flipud(C2)])

surf_full = ax.plot_surface(X_full, Y_full, Z_full, rstride=1, cstride=1,
                            facecolors=color_full, linewidth=0,
                            antialiased=False)


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

在此输入图像描述

在此输入图像描述

  • 相当大的盗窃确实......: - S (2认同)

mga*_*gab 7

回答

正如对问题的评论所指出的那样,matplotlib并没有真正进行3D绘图,它的近似可以给你有限的结果.您遇到的问题实际上已在mplot3d模块的常见问题解答中得到确认.

如果你想进行严肃的3D绘图,他们也会引导你去MayaVi.如果你真的不需要3D绘图而只关心顶视图,那么我会按照Bensciens在评论中的建议直接进行2D绘图...

肮脏的解决方法

当然,如果你愿意用程序员的灵魂付钱,几乎总会有一个涉及一些黑魔法的解决方案 ......:P

选项1

如果你真的只需要你放置的两个视图,并且表面与那些相似,你可以首先绘制表面A后面的部分,然后绘制所有表面B,然后绘制位于表面A顶部的部分. ... 让我解释:

正如指出的在这里这里 plot_surfaces()不关心面具,但你可以用NaN值来获得类似的效果.您可以使用它首先仅绘制低于另一个表面的值,然后仅绘制上面的值...

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

fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)

R = (X+Y)
Z1 = R/R.max()
Z2 = -R/R.max()

surfA_bottom = ax.plot_surface(X, Y, np.where(Z1<=Z2,Z1, np.nan),
                               rstride=1, cstride=1, color='r', linewidth=0)

surfB = ax.plot_surface(X, Y, Z2,
                        rstride=1, cstride=1, color='b', linewidth=0)

surfA_top = ax.plot_surface(X, Y, np.where(Z1>=Z2,Z1, np.nan),
                            rstride=1, cstride=1, color='r', linewidth=0)

ax.set_zlim3d(-1, 1)
ax.set_ylim(-5,5)
ax.set_xlim(-5,5)

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

3d绘图与蒙面数组1

3d绘图与蒙面数组2

选项2

(它有一些解释,如果你只想要解决方案,请跳到最后一段代码!)

这个解决方案稍微复杂一点,但对于更复杂的表面也更加健壮......事实上,3d图中的matplotlib处理不能很好地处理不同对象的深度......对吗?但它确实适用于单个物体......如何将两个表面绘制为单个表面,那么??

要执行此操作,您需要将所有点合并到单个曲面中(对于重复的XY组合,您可以有多个Z值).为了区分我们新表面的两个部分(我们以前的两个表面),我们可以使用facecolorskwarg.(我添加了一些alpha值来更清楚地看到发生了什么)

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

fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)

Z1 = np.sin(np.sqrt(X**2+Y**2))
Z2 = np.ones_like(Z1)*0.6

C1 = np.empty_like(X, dtype=str)
C1.fill('b')
C2 = C1.copy()
C2.fill('r')

X3 = np.vstack([X,X])
Y3 = np.vstack([Y,Y])
Z3 = np.vstack([Z1,Z2])
C3 = np.vstack([C1,C2])


surf3 = ax.plot_surface(X3, Y3, Z3, rstride=1, cstride=1,
                       facecolors=C3, linewidth=0,
                       antialiased=False, alpha=0.5)

ax.set_zlim3d(-1, 2)
plt.show()
Run Code Online (Sandbox Code Playgroud)

合并到一个阵列1的3d图

正如您所看到的,结果非常好但是有一些奇怪的效果,因为一个表面的一个极端连接到另一个表面的另一个极端.如何摆脱它?透明胶片不是一种选择,因为据我所知,plot_surface()只允许alpha影响整个表面的值.我还尝试使用X,Y和Z中的一行值以与解决方法1类似的方式屏蔽转换,但随后渲染被破坏.您可以尝试,也许这取决于我的安装.NaN

编辑:我找到了一个不那么优雅且问题更多的解决方案,但正如@will所指出的那样,你可以通过使用rgbasynthax 指定颜色来仅在桥接区域设置透明度.我将保留我的版本用于评论历史,因为答案已经足够长了......:P

(你可以获得更柔和的边缘,增加点数)

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

fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)

# Complex shape from examples in matplotlib gallery
Z1 = np.sin(np.sqrt(X**2+Y**2))
Z2 = np.ones_like(Z1)*0.6

# Define the color for each one of our surfaces
# (it doesn't need to be a gradient)
color1 = np.empty_like(X, dtype=str)
color1.fill('b')
color2 = np.empty_like(X, dtype=str)
color2.fill('r')

# Create a white bridge region
X_bridge = np.vstack([X[-1,:],X[0,:]])
Y_bridge = np.vstack([Y[-1,:],Y[0,:]])
Z_bridge = np.vstack([Z1[-1,:],Z2[0,:]])
color_bridge = np.empty_like(Z_bridge, dtype=object)
color_bridge.fill((1,1,1,0))

# Join the two surfaces (using also the bridge)
X_full = np.vstack([X, X_bridge, X])
Y_full = np.vstack([Y, Y_bridge, Y])
Z_full = np.vstack([Z1, Z_bridge, Z2])
color_full = np.vstack([color1, color_bridge, color2])

surf_full = ax.plot_surface(X_full, Y_full, Z_full, rstride=1, cstride=1,
                                    facecolors=color_full, linewidth=0,
                                                                antialiased=False)

ax.set_zlim3d(-1, 2)
ax.set_ylim(-5,5)
ax.set_xlim(-5,5)

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

在此输入图像描述

在此输入图像描述

  • 你几乎拥有它.差点儿.Matplotlib允许`rgba`颜色,所以你可以做`color_bridge = np.empty_like(Z_bridge,dtype = object)`然后`color_bridge.fill((1,1,1,0))`来填充那些透明的面孔颜色.完成. (2认同)