【问题标题】:Preserve content of fig after show() in matplotlib?在 matplotlib 中的 show() 之后保留 fig 的内容?
【发布时间】:2017-12-02 07:35:38
【问题描述】:

我正在创建一些数据的小提琴图,然后我将带有单个数据点(示例中的红点)的散点图渲染到三个子图。

由于小提琴图的生成相对耗时,我只生成一次小提琴图,然后为一个数据行添加散点图,写入结果文件,从轴上删除散点图并添加下一个散点图排。

一切正常,但我想添加选项,在保存之前显示()每个图。

如果我使用plt.show(),则该图显示正确,但之后该图似乎已清除,并且在下一次迭代中,我将获得没有小提琴图的图。

plt.show()之后有什么办法可以保留图的内容吗?

简而言之,我的代码是

fig = generate_plot(ws, show=False) #returns the fig instance of the violin plot

#if I do plt.show() here (or in "generate_plot()"), the violin plots are gone.

ax1, ax3, ax2 = fig.get_axes()
scatter1 = ax1.scatter(...) #draw scatter plot for first axes
[...] #same vor every axis
plt.savefig(...)
scatter1.remove()

【问题讨论】:

  • 我也有类似的情况。这个帖子有帮助吗?我现在正在读书。 stackoverflow.com/questions/21875356/…
  • plt.show() 意味着在脚本结束时只调用一次。之后,该图形从 pyplot 状态机中删除,这样对plt.show() 的第二次调用将不再显示该图形。您仍然可以使用fig.savefig() 而不是plt.savefig() 来保存它。但这无助于再次展示它。所以你最好使用交互模式或FuncAnimation 或事件循环内的一些事件处理。

标签: python python-3.x matplotlib


【解决方案1】:

我在想一个可能的选择是使用事件循环来推进情节。下面将定义一个更新函数,它只改变散点,绘制图像并保存它。我们可以通过一个在 key_press 上带有回调的类来管理这个 - 这样当你点击 Space 时,下一个图像就会显示出来;在最后一张图像上按 Space 后,绘图关闭。

import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
import numpy as np

class NextPlotter(object):
    def __init__(self, fig, func, n):
        self.__dict__.update(locals())
        self.i = 0
        self.cid = self.fig.canvas.mpl_connect("key_press_event", self.adv)

    def adv(self, evt):
        if evt.key == " " and self.i < self.n:
            self.func(self.i)
            self.i+=1
        elif self.i >= self.n:
            plt.close("all")

#Start of code:
# Create data
pos = [1, 2, 4, 5, 7, 8]
data = [np.random.normal(0, std, size=100) for std in pos]
data2 = [np.random.rayleigh(std, size=100) for std in pos]
scatterdata = np.random.normal(0, 5, size=(10,len(pos)))

#Create plot
fig, axes = plt.subplots(ncols=2)

axes[0].violinplot(data, pos, points=40, widths=0.9,
                      showmeans=True, showextrema=True, showmedians=True)
axes[1].violinplot(data2, pos, points=40, widths=0.9,
                      showmeans=True, showextrema=True, showmedians=True)

scatter = axes[0].scatter(pos, scatterdata[0,:], c="crimson", s=60)
scatter2 = axes[1].scatter(pos, scatterdata[1,:], c="crimson", s=60)  

# define updating function
def update(i):
    scatter.set_offsets(np.c_[pos,scatterdata[2*i,:]])
    scatter2.set_offsets(np.c_[pos,scatterdata[2*i+1,:]])
    fig.canvas.draw()
    plt.savefig("plot{i}.png".format(i=i))

# instantiate NextPlotter; press <space> to advance to the next image        
c = NextPlotter(fig, update, len(scatterdata)//2)

plt.show()

【讨论】:

  • 我认为最好在pos 下添加std = [1, 2, 4, 5, 7, 8],以便通过复制和粘贴来处理您的代码。这只是一条评论。
  • @dkato 它不起作用吗? std 应该是 8,因为它是 pos 中的最后一个条目。
  • 我得到以下信息: Traceback(最近一次调用最后一次):文件“~/test.py”,第 24 行,在 中 scatterdata = np.random.normal(0, std, size= (10,len(pos))) NameError: name 'std' is not defined
  • 也许,您需要修复 scatterdata 行。
  • 有趣,对我来说很好用。无论如何,只要输入一个数字即可。我编辑了答案。
【解决方案2】:

解决方法可能是不删除散点图。

为什么不保留散点图轴,只更新那组轴的数据?

在更新散点图数据后,您很可能需要plt.draw() 来强制进行新的渲染。

【讨论】:

  • 这可能是交换散点图的更简单的解决方案,但实际上我的解决方案的这一部分效果很好。我的问题是:如果我在添加散点图之前发出 plot.show() ,小提琴图就消失了。我将更新示例代码以澄清这一点。
  • 或者通过不同的方式你不再有原来的问题,所以只是不同的方式来实现相同的目标 - 情节你想要的。 plt.show() 在幕后做着各种各样的事情,最好尽量远离。
【解决方案3】:

我找到了一种交互式绘制图形的方法hereplt.ion() 并用input() 阻止进程似乎很重要。

import matplotlib.pyplot as plt
plt.ion()

fig = plt.figure()
ax = plt.subplot(1,1,1)
ax.set_xlim([-1, 5])
ax.set_ylim([-1, 5])
ax.grid('on')

for i in range(5):
    lineObject = ax.plot(i,i,'ro')
    fig.savefig('%02d.png'%i)
    # plt.draw() # not necessary?
    input()
    lineObject[0].remove()

我也尝试使用time.sleep(1) 阻止该进程,但它根本不起作用。

【讨论】:

  • 好吧,我仍然设法使用你的方法保存了所有无花果,但是 plt.show() 只显示一个空白屏幕(无论是否调用 draw())
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-24
  • 1970-01-01
  • 1970-01-01
  • 2020-07-04
相关资源
最近更新 更多