PCA交互式可视化代码报错:无法访问未赋值的自由变量'fig'
问题解决:调用plot_widget()触发NameError:无法访问自由变量'fig'
问题背景
运行PCA探索性数据分析Notebook时,调用plot_widget()函数触发NameError,报错显示无法访问封闭作用域中未关联值的自由变量fig,错误位于pca_utils.py的update函数第285行,尝试多种方法修复均未解决。
完整报错栈
--------------------------------------------------------------------------- NameError Traceback (most recent call last) File /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/ipywidgets/widgets/interaction.py:243, in interactive.update(self, *args) 241 value = widget.get_interact_value() 242 self.kwargs[widget._kwarg] = value --> 243 self.result = self.f(**self.kwargs) 244 show_inline_matplotlib_plots() 245 if self.auto_display and self.result is not None: File ~/Documents/GitHub/Machine_Learning/Machine_Learning_in_School/Lab8_PCA_ungraded/pca_utils.py:285, in plot_widget.<locals>.update(angle) 283 def update(angle): 284 ang = angle --> 285 with fig.batch_update(): 286 p0r = rotation_matrix(ang)@p0 287 p1r = rotation_matrix(ang)@p1 NameError: cannot access free variable 'fig' where it is not associated with a value in enclosing scope
Notebook相关代码
PCA - 探索性数据分析示例
本Notebook目标:
- 复现Andrew的PCA示例
- 可视化PCA在二维小数据集上的工作原理,了解并非所有投影都"理想"
- 可视化三维数据如何被包含在二维子空间中
- 使用PCA发现高维数据中的隐藏模式
导入库
import pandas as pd import numpy as np from sklearn.decomposition import PCA from pca_utils import plot_widget from bokeh.io import show, output_notebook from bokeh.plotting import figure import matplotlib.pyplot as plt import plotly.offline as py
[后续Notebook代码及中文说明保留原样]
参考的变量作用域示例(未解决问题)
曾参考Copilot提供的变量作用域示例,但问题仍未解决:
import matplotlib.pyplot as plt def create_plot(): # 在正确作用域内定义图形对象 fig, ax = plt.subplots() # 绘制数据 ax.plot([1, 2, 3], [1, 4, 9]) # 设置标签和标题 ax.set_xlabel('X-axis') ax.set_ylabel('Y-axis') ax.set_title('Sample Plot') # 显示图形 plt.show() return fig # 调用函数创建并显示图形 fig = create_plot()
解决方案
问题根源
报错核心是plot_widget内部定义的update函数尝试访问外部作用域的fig变量,但该变量未被正确捕获到闭包中,或者fig在plot_widget函数内的定义位置错误,导致update无法访问。
修复步骤
修改pca_utils.py中的plot_widget函数,确保fig变量在update函数的封闭作用域中被正确定义,并且通过闭包可以访问:
- 打开
pca_utils.py,找到plot_widget函数 - 确保
fig对象在update函数定义之前创建,并且处于同一个作用域内 - 若使用Bokeh的
figure,需确保fig被正确初始化并传递给闭包
示例修复后的plot_widget函数结构:
from bokeh.models import ColumnDataSource from ipywidgets import interact def plot_widget(data): # 1. 先创建fig对象,确保在update函数之前定义 fig = figure(title="PCA 旋转可视化", x_axis_label='X', y_axis_label='Y') # 初始化数据源 source = ColumnDataSource(data=dict(x=[], y=[])) fig.circle('x', 'y', source=source) # 2. 定义update函数,此时fig处于封闭作用域,可正常访问 def update(angle): ang = angle with fig.batch_update(): # 执行旋转逻辑 p0r = rotation_matrix(ang)@p0 p1r = rotation_matrix(ang)@p1 # 更新数据源 source.data = dict(x=p0r[:,0], y=p0r[:,1]) # 3. 返回交互组件 return interact(update, angle=(0, 360, 1))
关键说明
- 必须保证
fig在update函数定义之前被创建,这样闭包才能捕获到该变量 - 从报错中的
fig.batch_update()判断,fig是Bokeh的figure实例,需避免和Matplotlib的图形对象混淆 - 检查
plot_widget函数内是否有其他逻辑导致fig被重新赋值或作用域被改变
内容的提问来源于stack exchange,提问作者HongViet
相关产品推荐
相关产品推荐

