Python:是否有一种方法可以使用Matplotlib绘制“部分”曲面图?

8

我想使用Matplotlib绘制类似以下图片的“部分”表面图:example

请注意,它不是在X-Y平面上完整的网格,而是从顶视图中缺少一个角落。以下是我尝试过但没有成功的代码。

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

X = np.array([[0,1],
              [0,1,2],
              [0,1,2,3],
             ])
Y = np.array([[0,0],
              [1,1,1],
              [2,2,2,2],
             ])
Z = np.array([[0.5, 0.6],
              [0.7, 0.8, 0.9],
              [1.0, 1.1, 1.2, 1.3],
             ])
fig = pyplot.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X,Y,Z)

错误信息如下:
ValueError: 用序列设置数组元素。
希望能提供一些指导,谢谢!
3个回答

10

你可以在不想绘制的区域中使用np.nan值来轻松实现此操作。以下是示例的修改版本,但已经进行了剪裁,如下图所示:

enter image description here

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

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 = np.sqrt(X**2 + Y**2)
Z = np.sin(.5*R)

Z[X+Y>4.] = np.nan  # the diagonal slice

surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
                       linewidth=0, antialiased=False, vmin=-1, vmax=1)
ax.set_zlim(-1.01, 1.01)

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

fig.colorbar(surf, shrink=0.5, aspect=5)

plt.show()

注意这里我必须在绘图命令中使用vminvmax关键字,否则颜色缩放会被NaN抛出。


1
干净的解决方案!我试了很多次,但没有意识到我必须设置限制来处理缺失的值。 - Crebit
@Crebit:谢谢。似乎matplotlib应该能够在其极限计算中忽略nans。也许现在有一种方法可以做到这一点,否则可能值得提出错误报告/功能请求。 - tom10
1
我发现在Z中存在nan值时,matplotlib现在会自动创建部分表面图,但为了使表面图的颜色不被拉到nan温度,我可以像你在这里展示的那样,通过使用vmin=0vmax=1来强制设定非nan值的可行范围。谢谢! - develarist
为了获得适当的颜色范围,我使用了vmin=np.nanmin(Z)和vmax=np.nanmax(Z)。不过我不确定这是否在一般情况下都有效。 - Michał Kuczyński

4

编辑:请参见tom10的答案。他的解决方案是将被排除部分的值设置为np.nans。

看起来matplotlib的plot_surface不接受np.nans作为坐标,所以那也行不通。如果我正确理解了你的问题,那么我至少可以提供一个临时解决方案:

如果你将“剪切”点设置为该维度中的最后一个值,而不是像MEVIS3000建议的将它们设置为零,则你的2d数组将全部具有相同的大小,表面看起来就像在那里切割了。

我在你的示例中添加了更多数据点,以使其更清晰。这是起点(整个表面都可见):

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


X = np.array([[0, 1, 2, 3, 4, 5],
              [0, 1, 2, 3, 4, 5],
              [0, 1, 2, 3, 4, 5],
             ])
Y = np.array([[0, 0, 0, 0, 0, 0],
              [1, 1, 1, 1, 1, 1],
              [2, 2, 2, 2, 2, 2],
             ])
Z = np.array([[0.5, 0.4, 0.35, 0.32, 0.312, 0.3],
              [0.9, 0.7, 0.60, 0.55, 0.525, 0.5],
              [1.0, 1.1, 1.20, 1.30, 1.400, 1.5],
             ])


fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X,Y,Z)

plt.show()

由此产生的表面如下所示(从上面看): 图片1:包含原始数据的表面

现在,如果我们调整数组,以使我们用该行中的前一个值替换“缺失”的值,则可以省略表面的一部分:

X = np.array([[0, 1, 2, 2, 2, 2],  # Notice the sequence 0, 1, 2, 2, 2...
              [0, 1, 2, 3, 3, 3],  # Here starting from 3
              [0, 1, 2, 3, 4, 4],  # Here starting from 4
             ])
Y = np.array([[0, 0, 0, 0, 0, 0],  # Here we don't need to do anything
              [1, 1, 1, 1, 1, 1],
              [2, 2, 2, 2, 2, 2],
             ])
Z = np.array([[0.5, 0.4, 0.35, 0.35, 0.35, 0.35],  # Here like in X: repeats from 0.35
              [0.9, 0.7, 0.60, 0.55, 0.55, 0.55],
              [1.0, 1.1, 1.20, 1.30, 1.40, 1.40],
             ])

生成的图表如下(仍然是相同的视图):图片2:右下角被剪掉了 这不是一种漂亮的修复方式,但这是一种方法。我将留给您想办法自动化“截断”的问题。

0

子数组必须具有相同的长度。例如:

X = np.array([[0,1,0,0],
              [0,1,2,0],
              [0,1,2,3]
             ])

等等。


目前我的解决方案是用0填充这些空白,以使其至少能够工作。但是这些0仍然会出现在图表上。毕竟,0并不等同于“没有”。 - Shawn
啊,我明白了。但我认为通常不可能绘制不连续的东西。就我所知,三角矩阵确实包含零,对于“非三角形”的部分来说是这样的。 - MEVIS3000
哦,谢谢澄清!我只是想描述一下它的样子。我会编辑一下,这样就不会让人感到困惑了。 - Shawn

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