更新 xlim 时 Matplotlib 注释在动画期间消失
Matplotlib Annotations disappear during animation when updating xlim
我在使用 FuncAnimation 时遇到问题,我的注释在更新 xlim 后被删除了。下面是带有预览的代码
您可以在此处的 google 合作实验室中尝试代码 https://colab.research.google.com/drive/1NrM-ZnSQKhADccpjCbNeOC5PU8uXw-Sb?authuser=2#scrollTo=bcYtgNaTYJ3g
import os
import matplotlib.animation as ani
import matplotlib.pyplot as plt
from collections import deque
from typing import List
from IPython.display import Image
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
def create_animation_for_data(qty_lists: List[List[int]], gif_path, add_horizontal_guides=True, dynamic_y_axis=True,
dynamic_x_axis=True):
plt.style.use(['dark_background'])
fruits_to_color = ["red", "orange"]
qtys1, qty2 = qty_lists
times = [i for i in range(len(qtys1))]
artists = []
zoom = 0.5
first_image_path = 'assets/apple.png'
first_image = plt.imread(first_image_path)
first_offset_image = OffsetImage(first_image, zoom=zoom)
second_image_path = 'assets/orange.png'
second_image = plt.imread(second_image_path)
second_offset_image = OffsetImage(second_image, zoom=zoom)
# initializing a figure in
# which the graph will be plotted
my_dpi = 200
figsize_pixels = (650, 1000)
figsize = (int(figsize_pixels[0] / my_dpi), figsize_pixels[1] / my_dpi)
fig = plt.figure(figsize=figsize, dpi=my_dpi)
fig.set_tight_layout(True)
records_per_second = 1
seconds_show_on_screen = 30
max_width_on_screen = records_per_second * seconds_show_on_screen
ticks_every = 20
graph_max_y = 100
max_y_list = list(range(60, graph_max_y + 1, ticks_every * 2))
max_y_index = 0
max_y = max_y_list[max_y_index]
highest_max_y = max_y_list[- 1]
xlim_min = times[0]
if dynamic_x_axis:
xlim_max = times[max_width_on_screen - 1]
else:
xlim_max = times[-1]
axes_xlim = (xlim_min, xlim_max)
if dynamic_y_axis:
ylim_max = max_y
else:
ylim_max = highest_max_y
axes_ylim = (0, ylim_max)
ax1 = plt.axes(xlim=axes_xlim, ylim=axes_ylim)
# Set a title
plt.title('Qty over time', fontsize=20)
# Set axis labels
plt.xlabel('Time', fontsize=18)
plt.ylabel('Qty', fontsize=18)
plotlays, plotcols = [2], fruits_to_color
labels = ['Apple', 'Orange']
lines = []
for index in range(len(qty_lists)):
lobj = ax1.plot([], [], lw=5, color=plotcols[index],
label=labels[index])[0]
lines.append(lobj)
# Make sure your axis ticks are large enough to be easily read.
# You don't want your viewers squinting to read your plot.
plt.yticks(range(0, highest_max_y + 1, ticks_every), [str(x) for x in range(0, highest_max_y + 1, ticks_every)],
fontsize=14)
plt.xticks(fontsize=14)
# Provide tick lines across the plot to help your viewers trace along
# the axis ticks. Make sure that the lines are light and small so they
# don't obscure the primary data lines.
if add_horizontal_guides:
max_x = len(times)
for y in range(0, highest_max_y + 1, ticks_every):
plt.plot(times, [y] * max_x, "--",
lw=1, color="white", alpha=0.7)
# Do this after the plotting done above
ax1.set_ylim(*axes_ylim)
# Remove the tick marks; they are unnecessary with the tick lines we just plotted.
plt.tick_params(axis="both", which="both", bottom="off", top="off",
labelbottom="on", left="off", right="off", labelleft="on")
# empty list to store x and y axis values
xdata = deque()
ydata1 = deque()
ydata2 = deque()
first_image_annotation = AnnotationBbox(
first_offset_image, (times[0], 0), xycoords='data', frameon=False)
artists.append(ax1.add_artist(first_image_annotation))
second_image_annotation = AnnotationBbox(
second_offset_image, (times[0], 0), xycoords='data', frameon=False)
artists.append(ax1.add_artist(second_image_annotation))
ann_list = [
first_image_annotation,
second_image_annotation,
]
# animation function
def animate(i):
nonlocal max_y_index
nonlocal ann_list
# appending new points to x, y axes points list
x1_and_x2 = times[i]
xdata.append(x1_and_x2)
y1 = qty_lists[0][i]
ydata1.append(y1)
y2 = qty_lists[1][i]
ydata2.append(y2)
xlist = [xdata, xdata]
ylist = [ydata1, ydata2]
# If we have passed our max_width_on_screen
if len(xdata) > max_width_on_screen:
# Delete the oldest record
xdata.popleft()
max_x = max(xdata)
min_x = max_x - seconds_show_on_screen
# Update our x axis
if dynamic_x_axis:
ax1.set_xlim(min_x, max_x)
graph_max_y_data = max(ydata1[-1], ydata2[-1])
max_y_data = max_y_list[max_y_index]
while graph_max_y_data > max_y_data:
max_y_index += 1
max_y_data = max_y_list[max_y_index]
# Update our y axis
if dynamic_y_axis:
ax1.set_ylim(0, max_y_data)
ydata1.popleft()
ydata2.popleft()
first_image_annotation_xybox = (x1_and_x2, y1)
first_image_annotation.xybox = first_image_annotation_xybox
second_image_annotation_xybox = (x1_and_x2, y2)
second_image_annotation.xybox = second_image_annotation_xybox
for lnum, line in enumerate(lines):
# set data for each line separately.
line.set_data(xlist[lnum], ylist[lnum])
return lines, ann_list
# call the animator
anim = ani.FuncAnimation(fig, animate, frames=len(times), interval=30, blit=False)
# save the animation as gif file
anim.save(gif_path, writer='imagemagick', fps=2)
return os.path.abspath(gif_path)
static_axes_gif = 'FuncAnimation-annotated-static-axes.gif'
print(static_axes_gif)
animation_path_static_axes = create_animation_for_data(data_to_plot,
static_axes_gif,
dynamic_x_axis=False,
dynamic_y_axis=False)
Image(url=animation_path_static_axes)
static_x_gif = 'FuncAnimation-annotated-static-x.gif'
print(static_x_gif)
animation_path_static_x_axis = create_animation_for_data(data_to_plot,
static_x_gif,
dynamic_x_axis=False,
dynamic_y_axis=True)
Image(url=animation_path_static_x_axis)
static_y_gif = 'FuncAnimation-annotated-static-y.gif'
print(static_y_gif)
animation_path_static_y_axis = create_animation_for_data(data_to_plot,
static_y_gif,
dynamic_x_axis=True,
dynamic_y_axis=False)
Image(url=animation_path_static_y_axis)
dynamic_axes_gif = 'FuncAnimation-annotated-dynamic-axes.gif'
print(dynamic_axes_gif)
animation_path_dynamic_axes = create_animation_for_data(data_to_plot,
dynamic_axes_gif,
dynamic_x_axis=True,
dynamic_y_axis=True)
Image(url=animation_path_dynamic_axes)
我正在制作 4 个具有可变动态轴的图表(动态 = 在胺化期间更新轴):
- FuncAnimation-annotated-static-axes.gif
- xlim 和 ylim 是固定的
- FuncAnimation-annotated-static-x.gif
- xlim 已修复
- ylim 是动态的
- FuncAnimation-annotated-static-y.gif
- xlim 是动态的
- ylim 已修复
- FuncAnimation-注释动态-axes.gif
- xlim 和 ylim 是动态的
我的注释在xlim更新的两种情况下消失了:
- FuncAnimation-annotated-static-y.gif
- FuncAnimation-注释-动态-axes.gif
请注意,当 xlim 为静态时,不会发生这种情况:
- FuncAnimation-annotated-static-axes.gif
- FuncAnimation-annotated-static-x.gif
有谁知道为什么会这样或者如何在不删除注释的情况下更新 xlim?
如果有什么不清楚/措辞不当的地方请告诉我,因为我真的需要解决这个问题。
所以问题出在我移动注释的方式上。
这是修复:
# Don't do this - updating xlim will causing the annotation do disappear
# first_image_annotation_xybox = (x1_and_x2, y1)
# first_image_annotation.xybox = first_image_annotation_xybox
#
# second_image_annotation_xybox = (x1_and_x2, y2)
# second_image_annotation.xybox = second_image_annotation_xybox
for lnum, line in enumerate(lines):
# set data for each line separately.
line.set_data(xlist[lnum], ylist[lnum])
# Do this - Update our annotations
for ann in ann_list:
ann.remove()
ann_list = []
first_image_annotation = AnnotationBbox(
first_offset_image, (x1_and_x2, y1), xycoords='data', frameon=False)
ann_list.append(ax1.add_artist(first_image_annotation))
second_image_annotation = AnnotationBbox(
second_offset_image, (x1_and_x2, y2), xycoords='data', frameon=False)
ann_list.append(ax1.add_artist(second_image_annotation))
return lines, ann_list
其余代码相同。想知道为什么这发生在更新 xlim 而不是更新 ylim ¯\(ツ)/¯
我在使用 FuncAnimation 时遇到问题,我的注释在更新 xlim 后被删除了。下面是带有预览的代码
您可以在此处的 google 合作实验室中尝试代码 https://colab.research.google.com/drive/1NrM-ZnSQKhADccpjCbNeOC5PU8uXw-Sb?authuser=2#scrollTo=bcYtgNaTYJ3g
import os
import matplotlib.animation as ani
import matplotlib.pyplot as plt
from collections import deque
from typing import List
from IPython.display import Image
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
def create_animation_for_data(qty_lists: List[List[int]], gif_path, add_horizontal_guides=True, dynamic_y_axis=True,
dynamic_x_axis=True):
plt.style.use(['dark_background'])
fruits_to_color = ["red", "orange"]
qtys1, qty2 = qty_lists
times = [i for i in range(len(qtys1))]
artists = []
zoom = 0.5
first_image_path = 'assets/apple.png'
first_image = plt.imread(first_image_path)
first_offset_image = OffsetImage(first_image, zoom=zoom)
second_image_path = 'assets/orange.png'
second_image = plt.imread(second_image_path)
second_offset_image = OffsetImage(second_image, zoom=zoom)
# initializing a figure in
# which the graph will be plotted
my_dpi = 200
figsize_pixels = (650, 1000)
figsize = (int(figsize_pixels[0] / my_dpi), figsize_pixels[1] / my_dpi)
fig = plt.figure(figsize=figsize, dpi=my_dpi)
fig.set_tight_layout(True)
records_per_second = 1
seconds_show_on_screen = 30
max_width_on_screen = records_per_second * seconds_show_on_screen
ticks_every = 20
graph_max_y = 100
max_y_list = list(range(60, graph_max_y + 1, ticks_every * 2))
max_y_index = 0
max_y = max_y_list[max_y_index]
highest_max_y = max_y_list[- 1]
xlim_min = times[0]
if dynamic_x_axis:
xlim_max = times[max_width_on_screen - 1]
else:
xlim_max = times[-1]
axes_xlim = (xlim_min, xlim_max)
if dynamic_y_axis:
ylim_max = max_y
else:
ylim_max = highest_max_y
axes_ylim = (0, ylim_max)
ax1 = plt.axes(xlim=axes_xlim, ylim=axes_ylim)
# Set a title
plt.title('Qty over time', fontsize=20)
# Set axis labels
plt.xlabel('Time', fontsize=18)
plt.ylabel('Qty', fontsize=18)
plotlays, plotcols = [2], fruits_to_color
labels = ['Apple', 'Orange']
lines = []
for index in range(len(qty_lists)):
lobj = ax1.plot([], [], lw=5, color=plotcols[index],
label=labels[index])[0]
lines.append(lobj)
# Make sure your axis ticks are large enough to be easily read.
# You don't want your viewers squinting to read your plot.
plt.yticks(range(0, highest_max_y + 1, ticks_every), [str(x) for x in range(0, highest_max_y + 1, ticks_every)],
fontsize=14)
plt.xticks(fontsize=14)
# Provide tick lines across the plot to help your viewers trace along
# the axis ticks. Make sure that the lines are light and small so they
# don't obscure the primary data lines.
if add_horizontal_guides:
max_x = len(times)
for y in range(0, highest_max_y + 1, ticks_every):
plt.plot(times, [y] * max_x, "--",
lw=1, color="white", alpha=0.7)
# Do this after the plotting done above
ax1.set_ylim(*axes_ylim)
# Remove the tick marks; they are unnecessary with the tick lines we just plotted.
plt.tick_params(axis="both", which="both", bottom="off", top="off",
labelbottom="on", left="off", right="off", labelleft="on")
# empty list to store x and y axis values
xdata = deque()
ydata1 = deque()
ydata2 = deque()
first_image_annotation = AnnotationBbox(
first_offset_image, (times[0], 0), xycoords='data', frameon=False)
artists.append(ax1.add_artist(first_image_annotation))
second_image_annotation = AnnotationBbox(
second_offset_image, (times[0], 0), xycoords='data', frameon=False)
artists.append(ax1.add_artist(second_image_annotation))
ann_list = [
first_image_annotation,
second_image_annotation,
]
# animation function
def animate(i):
nonlocal max_y_index
nonlocal ann_list
# appending new points to x, y axes points list
x1_and_x2 = times[i]
xdata.append(x1_and_x2)
y1 = qty_lists[0][i]
ydata1.append(y1)
y2 = qty_lists[1][i]
ydata2.append(y2)
xlist = [xdata, xdata]
ylist = [ydata1, ydata2]
# If we have passed our max_width_on_screen
if len(xdata) > max_width_on_screen:
# Delete the oldest record
xdata.popleft()
max_x = max(xdata)
min_x = max_x - seconds_show_on_screen
# Update our x axis
if dynamic_x_axis:
ax1.set_xlim(min_x, max_x)
graph_max_y_data = max(ydata1[-1], ydata2[-1])
max_y_data = max_y_list[max_y_index]
while graph_max_y_data > max_y_data:
max_y_index += 1
max_y_data = max_y_list[max_y_index]
# Update our y axis
if dynamic_y_axis:
ax1.set_ylim(0, max_y_data)
ydata1.popleft()
ydata2.popleft()
first_image_annotation_xybox = (x1_and_x2, y1)
first_image_annotation.xybox = first_image_annotation_xybox
second_image_annotation_xybox = (x1_and_x2, y2)
second_image_annotation.xybox = second_image_annotation_xybox
for lnum, line in enumerate(lines):
# set data for each line separately.
line.set_data(xlist[lnum], ylist[lnum])
return lines, ann_list
# call the animator
anim = ani.FuncAnimation(fig, animate, frames=len(times), interval=30, blit=False)
# save the animation as gif file
anim.save(gif_path, writer='imagemagick', fps=2)
return os.path.abspath(gif_path)
static_axes_gif = 'FuncAnimation-annotated-static-axes.gif'
print(static_axes_gif)
animation_path_static_axes = create_animation_for_data(data_to_plot,
static_axes_gif,
dynamic_x_axis=False,
dynamic_y_axis=False)
Image(url=animation_path_static_axes)
static_x_gif = 'FuncAnimation-annotated-static-x.gif'
print(static_x_gif)
animation_path_static_x_axis = create_animation_for_data(data_to_plot,
static_x_gif,
dynamic_x_axis=False,
dynamic_y_axis=True)
Image(url=animation_path_static_x_axis)
static_y_gif = 'FuncAnimation-annotated-static-y.gif'
print(static_y_gif)
animation_path_static_y_axis = create_animation_for_data(data_to_plot,
static_y_gif,
dynamic_x_axis=True,
dynamic_y_axis=False)
Image(url=animation_path_static_y_axis)
dynamic_axes_gif = 'FuncAnimation-annotated-dynamic-axes.gif'
print(dynamic_axes_gif)
animation_path_dynamic_axes = create_animation_for_data(data_to_plot,
dynamic_axes_gif,
dynamic_x_axis=True,
dynamic_y_axis=True)
Image(url=animation_path_dynamic_axes)
我正在制作 4 个具有可变动态轴的图表(动态 = 在胺化期间更新轴):
- FuncAnimation-annotated-static-axes.gif
- xlim 和 ylim 是固定的
- FuncAnimation-annotated-static-x.gif
- xlim 已修复
- ylim 是动态的
- FuncAnimation-annotated-static-y.gif
- xlim 是动态的
- ylim 已修复
- FuncAnimation-注释动态-axes.gif
- xlim 和 ylim 是动态的
我的注释在xlim更新的两种情况下消失了:
- FuncAnimation-annotated-static-y.gif
- FuncAnimation-注释-动态-axes.gif
请注意,当 xlim 为静态时,不会发生这种情况:
- FuncAnimation-annotated-static-axes.gif
- FuncAnimation-annotated-static-x.gif
有谁知道为什么会这样或者如何在不删除注释的情况下更新 xlim?
如果有什么不清楚/措辞不当的地方请告诉我,因为我真的需要解决这个问题。
所以问题出在我移动注释的方式上。 这是修复:
# Don't do this - updating xlim will causing the annotation do disappear
# first_image_annotation_xybox = (x1_and_x2, y1)
# first_image_annotation.xybox = first_image_annotation_xybox
#
# second_image_annotation_xybox = (x1_and_x2, y2)
# second_image_annotation.xybox = second_image_annotation_xybox
for lnum, line in enumerate(lines):
# set data for each line separately.
line.set_data(xlist[lnum], ylist[lnum])
# Do this - Update our annotations
for ann in ann_list:
ann.remove()
ann_list = []
first_image_annotation = AnnotationBbox(
first_offset_image, (x1_and_x2, y1), xycoords='data', frameon=False)
ann_list.append(ax1.add_artist(first_image_annotation))
second_image_annotation = AnnotationBbox(
second_offset_image, (x1_and_x2, y2), xycoords='data', frameon=False)
ann_list.append(ax1.add_artist(second_image_annotation))
return lines, ann_list
其余代码相同。想知道为什么这发生在更新 xlim 而不是更新 ylim ¯\(ツ)/¯