Matplotlib 3D散点图颜色在重绘后丢失

14
这个问题相关,我想要一个带有每个点预设颜色的三维散点图。该问题中发布的示例在我的系统上运行良好,但在第一次重绘之后(例如保存或旋转图像后),颜色似乎丢失了,即所有点都以蓝色绘制,并添加常规深度信息。请参见下面的修改示例。
我的系统是Python 2.6.7,使用从Macports安装的matplotlib 1.1.0,在Mac 10.8.0上使用MacOSX后端。
有人知道如何解决这个问题吗?
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Create Map
cm = plt.get_cmap("RdYlGn")

x = np.random.rand(30)
y = np.random.rand(30)
z = np.random.rand(30)

col = np.arange(30)

fig = plt.figure()
ax3D = fig.add_subplot(111, projection='3d')
ax3D.scatter(x, y, z, s=30, c=col, marker='o', cmap=cm)

plt.savefig('image1.png')
plt.savefig('image2.png')

这是我得到的两张图片: 第一张图片 第二张图片


嗯,在Windows上也是一样。如果我注释掉 col = np.arange(30) 这一行,两个图形将完全相同。虽然我不知道原因。pyplot 是一个有状态的模块,我认为在不关闭旧窗口的情况下打开新窗口是不明智的。如果始终只保持一个图形窗口打开,我认为可以预防这种效果。 - Björn Pollex
我刚刚注意到代码实际上使用了两个数字。尽管如此,这并不是问题的原因。我从示例代码中删除了第一个数字,但问题仍然存在。感谢您指出该问题在Windows上也存在。对我来说,这似乎是某种错误。 - David Zwicker
只是作为对未来访问者的一点提示,这个问题在MatPlotLib 1.2.0中似乎已经被修复了 :) - Poik
1个回答

11

目前还不清楚为什么会发生这种情况,但肯定是一个错误。在这里,我提供了一种方法来获得您想要的结果,尽管它不像人们想象的那样自动。

由于某些原因,在第一次渲染后,代表散点的Patch3DCollection没有被更新。这个更新是必不可少的,因为它设置了每个集合补丁的唯一颜色。为了强制它重新初始化,您可以使用Patch3DCollection(实际上是ScalarMappable方法)上的changed方法,并且这只是记录了一个变化。当绘制图形时,它会检查是否发生了更新,然后重新定义颜色。如果没有,则跳过此过程。

要自动强制执行此更新,希望在每个“绘制”事件上执行此操作。要做到这一点,必须使用canvasmpl_connect方法register a method注册一个方法(请参见链接的教程)。

这个例子展示了如何保存图形两次以保留颜色映射,但如果你取消注释 plt.show() 这一行,它仍然可以工作(例如在旋转时)。
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Create Map
cm = plt.get_cmap("RdYlGn")

# added a seed so consistant plotting of points
np.random.seed(101)
x = np.random.rand(30)
y = np.random.rand(30)
z = np.random.rand(30)

col = np.arange(30)

fig = plt.figure()
#ax = fig.add_subplot(111)
#scatCollection = ax.scatter(x,y,
ax3D = fig.add_subplot(111, projection='3d')
# keep track of the Patch3DCollection:
scatCollection = ax3D.scatter(x, y, z, s=30, 
                            c=col, 
                            marker='o',
                            cmap=cm
                            )
def forceUpdate(event):
    global scatCollection
    scatCollection.changed()

fig.canvas.mpl_connect('draw_event',forceUpdate)

#plt.show()

plt.savefig('image1.png')

plt.savefig('image2.png')

理想情况下不应该需要这样做,全局的scatCollection应该使用其他方法访问(我正在努力实现这一点)。但是现在这个方法可以工作...

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