python matplotlib在动画的多个子图中共享了xlabel描述/标题

问题描述

我正在使用以下代码使用matplotlib制作动画,以使实验可视化。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation,PillowWriter

plt.rcParams['animation.html'] = 'jshtml'

def make_grid(X,description=None,labels=None,title_fmt="label: {}",cmap='gray',ncols=3,colors=None):
    L = len(X)
    nrows = -(-L // ncols)
    frame_plot = []
    for i in range(L):
        plt.subplot(nrows,ncols,i + 1)
        im = plt.imshow(X[i].squeeze(),cmap=cmap,interpolation='none')
        if labels is not None:
            color = 'k' if colors is None else colors[i]
            plt.title(title_fmt.format(labels[i]),color=color)
        plt.xticks([])
        plt.yticks([])
        frame_plot.append(im)
    return frame_plot


def animate_step(X):
    return X ** 2

n_splots = 6
X = np.random.random((n_splots,32,3))

Y = X
X_t = []

for i in range(10):
    Y = animate_step(Y)
    X_t.append((Y,i))

frames = []
for X,step in X_t:
    frame = make_grid(X,description="step={}".format(step),labels=range(n_splots),title_fmt="target: {}")
    frames.append(frame)

anim = ArtistAnimation(plt.gcf(),frames,interval=300,repeat_delay=8000,blit=True)
plt.close()                               
anim.save("test.gif",writer=PillowWriter())
anim

结果可以在这里看到: https://i.stack.imgur.com/OaOsf.gif

到目前为止,它仍然可以正常工作,但是我很难获得共享的xlabel来为动画中的所有6个子图添加描述。应该显示图像在哪一步上,即“ step = 5”。 由于它是动画,因此我不能使用xlabel或set_title(因为在整个动画中它是恒定的),因此必须自己绘制文本。 我已经尝试过类似的方法。

def make_grid(X,colors=None):
    L = len(X)
    nrows = -(-L // ncols)
    frame_plot = []
    desc = plt.text(0.5,.04,description,size=plt.rcparams["axes.titlesize"],ha="center",transform=plt.gca().transAxes
                    )
    frame_plot.append(desc)
...

这当然是行不通的,因为尚未创建轴。我尝试使用另一个子图(nrows,1,nrows)的轴,但随后绘制了现有图像。

有人对此有解决方案吗?

编辑:

目前不干净,hacky解决方案: 等待最后一行的中间图像的轴创建,然后使用它来绘制文本。 在for循环中:

...
        if i == int((nrows - 0.5) * ncols):
            title = ax.text(0.25,-.3,size=plt.rcParams["axes.titlesize"],# ha="center",transform=ax.transAxes
                            )
            frame_plot.append(title)
...

解决方法

对我来说,即使您已经可以访问要显示动画的完整数据列表,也可以使用FuncAnimation而不是ArtistAnimation来解决您的案件(请参见this thread讨论两个功能之间的区别。

this FuncAnimation example的启发,我在下面编写了满足您需要的代码(将相同的代码与ArtistAnimation一起使用,正确的参数列表无效)。

主要思想是初始化所有要在开始时进行动画处理的元素,并在动画帧上对其进行更新。可以对负责显示当前步骤的文本对象(step_txt = fig.text(...))和ax.imshow中的图像进行此操作。然后,您可以使用此食谱更新想要显示动画的任何对象。

请注意,如果您希望文本为x_label或您选择显示的任何文本,则该方法有效。参见代码中的注释行。

#!/Users/seydoux/anaconda3/envs/jupyter/bin/python
import numpy as np
import matplotlib.pyplot as plt

from matplotlib.animation import FuncAnimation,PillowWriter

# parameters
n_frames = 10
n_splots = 6
n_cols = 3
n_rows = n_splots // n_cols


def update_data(x):
    return x ** 2


# create all snapshots
snapshots = [np.random.rand(n_splots,32,3)]
for _ in range(n_frames):
    snapshots.append(update_data(snapshots[-1]))

# initialize figure and static elements
fig,axes = plt.subplots(2,3)
axes = axes.ravel()  # so we can access all axes with a single index
for i,ax in enumerate(axes):
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_title("target: {}".format(i))

# initialize elements to be animated
step_txt = fig.text(0.5,0.95,"step: 0",ha="center",weight="bold")
# step_txt = axes[4].set_xlabel("step: 0")  # also works with x_label
imgs = list()
for a,s in zip(axes,snapshots[0]):
    imgs.append(a.imshow(s,interpolation="none",cmap="gray"))


# animation function
def animate(i):

    # update images
    for img,s in zip(imgs,snapshots[i]):
        img.set_data(s)

    # update text
    step_txt.set_text("step: {}".format(i))

    # etc


anim = FuncAnimation(fig,animate,frames=n_frames,interval=300)
anim.save("test.gif",writer=PillowWriter())

这是我从上面的代码中获得的输出:

animated with step display

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...