【问题标题】:python matplotlib shared xlabel description / title on multiple subplots for animationpython matplotlib在多个动画子图上共享xlabel描述/标题
【发布时间】:2020-09-29 15:34:32
【问题描述】:

我正在使用以下代码使用 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,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, description=None, labels=None, title_fmt="label: {}", cmap='gray', ncols=3, 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, description,
                            size=plt.rcParams["axes.titlesize"],
                            # ha="center",
                            transform=ax.transAxes
                            )
            frame_plot.append(title)
...

【问题讨论】:

    标签: python matplotlib animation title subplot


    【解决方案1】:

    对我来说,使用 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, 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())
    

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

    【讨论】:

    • 这是我认为拥有这个小功能会很酷的问题之一;我最多只需要 15 分钟.. 2 天后我就要砸我的电脑了。非常感谢。
    猜你喜欢
    • 2014-02-12
    • 2018-01-19
    • 1970-01-01
    • 2017-09-23
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多