You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 10:00:13