有没有办法在 matplotlib 中标记多个 3d 表面?
Posted
技术标签:
【中文标题】有没有办法在 matplotlib 中标记多个 3d 表面?【英文标题】:Is there a way to label multiple 3d surfaces in matplotlib? 【发布时间】:2019-08-27 03:26:24 【问题描述】:我尝试解决具有线性约束的非线性数学优化问题。为此,我尝试在 3d 中可视化约束,以了解发生了什么以及为什么我为约束中的某些参数而不是其他参数获得了可行的解决方案。
为了实现这一点,我想使用 python 中的 matplotlib 来生成 3d 曲面(平面,因为我的所有约束都是线性的)。
但是,如果没有绘图内标记,则很难识别哪个曲面属于哪个约束。这导致我想寻找一种方法来在情节中添加带有颜色的图例。
我认识到已经有一种方法可以在 2D 中执行此操作,在方法 ax.plot()
或 ax.scatter()
中,但尝试对 ax.plot_surface(X, Y, Z, label = 'mylabel')
执行相同操作却不起作用
完整的脚本如下:
from mpl_toolkits import mplot3d
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = plt.axes(projection='3d')
plt.rcParams['legend.fontsize'] = 10
# First constraint
g2 = np.linspace(-5,5,2)
g3 = np.linspace(-5,5,2)
G2,G3 = np.meshgrid(g2,g3)
G4_1 = -1.18301270189222 - 0.5*G2 + 0.5*G3
ax = fig.gca(projection='3d')
c1 = ax.plot_surface(G2, G3, G4_1, label = "c1")
# Second
G3, G4 = np.meshgrid(g2, g3)
G2 = G3
c2 = ax.plot_surface(G2, G3, G4, label = "c2")
# Third
G2,G3 = np.meshgrid(g2,g3)
G4 = (0.408248290463863*G2 + 0.408248290463863*G3 -0.707106781186548)/1.63299316185545
c3 = ax.plot_surface(G2, G3, G4, label = "c3")
# And forth
G4 = (1.04903810567666 - (0.288675134594813*G2 + 0.288675134594813*G3))/0.577350269189626
c4 = ax.plot_surface(G2, G3, G4, label="c4")
ax.legend() # -> error : 'AttributeError: 'Poly3DCollection' object has no attribute '_edgecolors2d''
# labeling the figure
fig.suptitle("Constraints")
#plt.xlabel('g2', fontsize=14)
#plt.ylabel('g3', fontsize=14)
ax.set_xlabel(r'$g_2$', fontsize=15, rotation=60)
ax.set_ylabel('$g_3$', fontsize=15, rotation=60)
ax.set_zlabel('$g_4$', fontsize=15, rotation=60)
plt.savefig('Constraints.jpg')
plt.show()
结果如下图。
你可能已经看到了,没有办法分辨哪个表面属于哪个约束,而我想要实现的是一个图例,比如here。
我通读了this question 的答案,但它在这里不起作用,因为我有多个表面。试了之后,一直只显示一个标签,而不是四个。
所以我的问题是,有没有办法在我的 ax.plot_surface
或任何其他合适的 hack 中添加图例?
【问题讨论】:
【参考方案1】:你去。
解决方案在此错误中:ax.legend() # -> error : 'AttributeError: 'Poly3DCollection' object has no attribute '_edgecolors2d''
。
我相信这是一个错误。
如果您探索任何表面对象(比如说c1
),您会发现它们具有'_edgecolors3d'
属性,这是在创建图例时应该调用的属性。
所以我们只需创建一个名为'_edgecolors2d'
的新属性,其内容与'_edgecolors3d'
相同。
一旦'_edgecolors2d'
问题得到解决,您将遇到'_facecolors2d'
的新问题。我们重复同样的过程,我们就完成了。
fig = plt.figure()
ax = plt.axes(projection='3d')
plt.rcParams['legend.fontsize'] = 10
# First constraint
g2 = np.linspace(-5,5,2)
g3 = np.linspace(-5,5,2)
G2,G3 = np.meshgrid(g2,g3)
G4_1 = -1.18301270189222 - 0.5*G2 + 0.5*G3
ax = fig.gca(projection='3d')
c1 = ax.plot_surface(G2, G3, G4_1, label = "c1")
c1._facecolors2d=c1._facecolors3d
c1._edgecolors2d=c1._edgecolors3d
# Second
G3, G4 = np.meshgrid(g2, g3)
G2 = G3
c2 = ax.plot_surface(G2, G3, G4, label = "c2")
c2._facecolors2d=c2._facecolors3d
c2._edgecolors2d=c2._edgecolors3d
# Third
G2,G3 = np.meshgrid(g2,g3)
G4 = (0.408248290463863*G2 + 0.408248290463863*G3 -0.707106781186548)/1.63299316185545
c3 = ax.plot_surface(G2, G3, G4, label = "c3")
c3._facecolors2d=c3._facecolors3d
c3._edgecolors2d=c3._edgecolors3d
# And forth
G4 = (1.04903810567666 - (0.288675134594813*G2 + 0.288675134594813*G3))/0.577350269189626
c4 = ax.plot_surface(G2, G3, G4, label="c4")
c4._facecolors2d=c4._facecolors3d
c4._edgecolors2d=c4._edgecolors3d
ax.legend() # -> error : 'AttributeError: 'Poly3DCollection' object has no attribute '_edgecolors2d''
# labeling the figure
fig.suptitle("Constraints")
#plt.xlabel('g2', fontsize=14)
#plt.ylabel('g3', fontsize=14)
ax.set_xlabel(r'$g_2$', fontsize=15, rotation=60)
ax.set_ylabel('$g_3$', fontsize=15, rotation=60)
ax.set_zlabel('$g_4$', fontsize=15, rotation=60)
plt.savefig('Constraints.jpg')
plt.show()
【讨论】:
this issue 一定是被人遗忘了。【参考方案2】:这是对@Gio's answer 的更新。截至matplotlib 3.3.3
、_facecolors3d
和_edgecolors3d
不存在。所以,而不是这个:
c1._facecolors2d = c1._facecolors3d
c1._edgecolors2d = c1._edgecolors3d
这会导致类似的AttributeError
,试试这个:
c1._facecolors2d = c1._facecolor3d
c1._edgecolors2d = c1._edgecolor3d
由于代表人数少,我不得不将此作为答案,而不是评论。
【讨论】:
以上是关于有没有办法在 matplotlib 中标记多个 3d 表面?的主要内容,如果未能解决你的问题,请参考以下文章