Python实现持久同伦动画报错排查:AttributeError问题求解
持久同伦动画代码错误分析与修复
原代码
import numpy as np from ripser import ripser import matplotlib.pyplot as plt import matplotlib.animation as animation np.random.seed(0) data = np.random.rand(50, 2) diagrams = ripser(data)['dgms'] fig, ax = plt.subplots() scat = ax.scatter(data[:, 0], data[:, 1], c='black') bars = [ax.bar([], [], color='blue', alpha=0.5) for i in range(len(diagrams))] def update(frame): for i, bar in enumerate(bars): ax.collections.clear() scat = ax.scatter(data[:, 0], data[:, 1], c='black') bar.clear() bar_data = diagrams[i] bar_data = sorted(bar_data, key=lambda x: x[1] - x[0], reverse=True) for j, (birth, death) in enumerate(bar_data): bar.add_patch(plt.Rectangle((j + 0.1, birth), 0.8, death - birth, alpha=0.5, color='blue')) ax.set_title(f"Frame {frame}, Homology Dimension {i}") return scat, bars ani = animation.FuncAnimation(fig, update, frames=range(len(diagrams[0])), blit=True) plt.show()
出现的错误
AttributeError: 'FuncAnimation' object has no attribute '_resize_id'AttributeError: 'ArtistList' object has no attribute 'clear'
错误原因
1. ArtistList 无 clear() 方法
ax.bar() 返回的是 matplotlib.container.BarContainer(属于ArtistList子类),这个对象没有clear()方法。调用bar.clear()会直接触发错误,正确做法是每次更新时移除之前的bar元素,而非调用clear。
2. FuncAnimation 的 _resize_id 属性缺失
这个错误和blit=True的使用直接相关:
- 旧版本matplotlib中,
FuncAnimation没有_resize_id属性,开启blit会触发该错误; - 非交互式环境(如脚本运行)中,blit的兼容性也容易出问题。另外
update函数返回的格式不符合blit要求——返回单个对象加列表,而blit需要扁平化的艺术家迭代器。
修正后的代码
import numpy as np from ripser import ripser import matplotlib.pyplot as plt import matplotlib.animation as animation np.random.seed(0) data = np.random.rand(50, 2) diagrams = ripser(data)['dgms'] fig, ax = plt.subplots() # 初始化散点图 scat = ax.scatter(data[:, 0], data[:, 1], c='black') # 用列表存储bar容器,而非提前创建空bar bar_containers = [] def update(frame): # 清除之前的bar元素 for container in bar_containers: for patch in container: patch.remove() bar_containers.clear() # 遍历每个同伦维度 for i, dgm in enumerate(diagrams): ax.clear() # 重新绘制散点图 ax.scatter(data[:, 0], data[:, 1], c='black') # 按持久化长度排序 sorted_dgm = sorted(dgm, key=lambda x: x[1] - x[0], reverse=True) # 创建bar if len(sorted_dgm) > 0: births = [x[0] for x in sorted_dgm] lengths = [x[1] - x[0] for x in sorted_dgm] container = ax.bar(range(len(sorted_dgm)), lengths, bottom=births, color='blue', alpha=0.5, width=0.8) bar_containers.append(container) ax.set_title(f"Frame {frame}, Homology Dimension {i}") ax.set_xticks(range(len(sorted_dgm))) ax.set_ylabel("Filtration Value") return scat, *[patch for container in bar_containers for patch in container] # 关闭blit避免版本兼容问题 ani = animation.FuncAnimation(fig, update, frames=range(len(diagrams[0])), blit=False) plt.show()
关键修改点
- 移除
bar.clear()调用,改为每次更新时删除旧bar补丁并清空容器列表; - 关闭
blit=True,规避版本兼容问题; - 优化散点图绘制逻辑,避免重复清除所有collections;
- 调整返回值格式,确保所有需要更新的艺术家对象被正确返回。
内容的提问来源于stack exchange,提问作者The Mad Scientist
相关产品推荐
相关产品推荐

