python matplotlib 在多个动画子图上共享 xlabel 描述/标题
python matplotlib shared xlabel description / title on multiple subplots for animation
我正在使用以下代码使用 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),但是现有图像被绘制了..
有人对此有解决方案吗?
编辑:
目前不干净、老套的解决方案:
等待创建最后一行中间图像的轴,并将其用于绘制文本。
在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)
...
对我来说,使用 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())
这是我从上面的代码得到的输出:
我正在使用以下代码使用 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),但是现有图像被绘制了..
有人对此有解决方案吗?
编辑:
目前不干净、老套的解决方案: 等待创建最后一行中间图像的轴,并将其用于绘制文本。 在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)
...
对我来说,使用 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())
这是我从上面的代码得到的输出: