VS Code中Jupyter Notebook高效处理千余需用户输入的绘图
解决方案
1. 在Jupyter Notebook中高效管理超500张图表
Jupyter输出中断的核心原因是未清理的图表占据大量内存,以及默认输出缓冲区限制。可以通过以下方法解决:
(1)实时清理图表内存
每次显示图表后手动关闭当前图表对象,释放内存,避免内存堆积。修改后的绘图代码如下:
import pandas as pd import numpy as np import matplotlib.pyplot as plt # 常量定义 ID_COLUMN = 'ID' Z_COLUMN = 'Z' X_COLUMN = 'X' Y_COLUMN = 'Y' # 示例数据 data = pd.DataFrame({ ID_COLUMN: np.random.choice(['A', 'B', 'C'], 1000), Z_COLUMN: np.random.choice([10, 20, 30], 1000), X_COLUMN: np.random.rand(1000) * 100, Y_COLUMN: np.random.rand(1000) * 50 }) def plot_data(data): unique_ids = data[ID_COLUMN].unique() # 记录已处理项,用于中断后恢复 processed = [] # 加载已处理记录(如果存在) try: processed_df = pd.read_csv('processed_records.csv') processed = list(zip(processed_df['ID'], processed_df['Z'])) except FileNotFoundError: pass for data_id in unique_ids: filtered_data = data[data[ID_COLUMN] == data_id] unique_z_values = filtered_data[Z_COLUMN].unique() for z_value in unique_z_values: if (data_id, z_value) in processed: continue filtered_data_z = filtered_data[filtered_data[Z_COLUMN] == z_value] # 创建图表对象并赋值,方便后续关闭 fig = plt.figure() plt.plot(filtered_data_z[X_COLUMN], filtered_data_z[Y_COLUMN]) plt.title(f'ID: {data_id}, Z: {z_value}') plt.xlabel(X_COLUMN) plt.ylabel(Y_COLUMN) plt.show() # 关闭当前图表,释放内存 plt.close(fig) user_input = input(f'Accept plot for ID {data_id} at Z {z_value}? (y/n/abort): ') if user_input.lower() == 'abort': pd.DataFrame(processed, columns=['ID', 'Z']).to_csv('processed_records.csv', index=False) return processed.append((data_id, z_value)) plot_data(data)
(2)优化Jupyter显示配置
在Notebook开头执行以下命令,降低内存占用并提升输出限制:
- 改用轻量化图片格式:
%config InlineBackend.figure_format='png' - 提升输出块数量上限:
%config IPython.display.max_output_sections=1000
2. 大规模带用户输入绘图的最佳实践与替代方案
(1)用Jupyter交互式组件替代原生input()
原生input()在大量输入场景下易阻塞,体验差。改用ipywidgets构建交互式界面,更稳定且能自动记录反馈:
import pandas as pd import numpy as np import matplotlib.pyplot as plt import ipywidgets as widgets from IPython.display import display, clear_output ID_COLUMN = 'ID' Z_COLUMN = 'Z' X_COLUMN = 'X' Y_COLUMN = 'Y' data = pd.DataFrame({ ID_COLUMN: np.random.choice(['A', 'B', 'C'], 1000), Z_COLUMN: np.random.choice([10, 20, 30], 1000), X_COLUMN: np.random.rand(1000) * 100, Y_COLUMN: np.random.rand(1000) * 50 }) # 存储反馈结果 feedback_records = [] # 创建交互按钮 accept_btn = widgets.Button(description='Accept') reject_btn = widgets.Button(description='Reject') abort_btn = widgets.Button(description='Abort') output = widgets.Output() def on_accept(b): with output: clear_output(wait=True) feedback_records.append((current_id, current_z, 'accept')) next_plot() def on_reject(b): with output: clear_output(wait=True) feedback_records.append((current_id, current_z, 'reject')) next_plot() def on_abort(b): with output: clear_output(wait=True) pd.DataFrame(feedback_records, columns=['ID', 'Z', 'Feedback']).to_csv('feedback.csv', index=False) print('Aborted, feedback saved.') accept_btn.on_click(on_accept) reject_btn.on_click(on_reject) abort_btn.on_click(on_abort) # 生成所有待处理的(ID, Z)对 pending_items = [] for data_id in data[ID_COLUMN].unique(): filtered_data = data[data[ID_COLUMN] == data_id] for z_value in filtered_data[Z_COLUMN].unique(): pending_items.append((data_id, z_value)) current_index = 0 current_id, current_z = pending_items[current_index] def next_plot(): global current_index, current_id, current_z if current_index >= len(pending_items)-1: with output: clear_output(wait=True) pd.DataFrame(feedback_records, columns=['ID', 'Z', 'Feedback']).to_csv('feedback.csv', index=False) print('All plots processed!') return current_index += 1 current_id, current_z = pending_items[current_index] plot_current() def plot_current(): with output: clear_output(wait=True) filtered_data_z = data[(data[ID_COLUMN]==current_id) & (data[Z_COLUMN]==current_z)] fig = plt.figure() plt.plot(filtered_data_z[X_COLUMN], filtered_data_z[Y_COLUMN]) plt.title(f'ID: {current_id}, Z: {current_z}') plt.xlabel(X_COLUMN) plt.ylabel(Y_COLUMN) plt.show() plt.close(fig) # 显示界面 display(widgets.HBox([accept_btn, reject_btn, abort_btn])) display(output) plot_current()
(2)预生成图表文件,批量审核
先将所有图表保存为本地文件,再用脚本批量浏览收集反馈,完全避开Jupyter的输出限制:
# 第一步:批量生成图表文件 import pandas as pd import numpy as np import matplotlib.pyplot as plt import os ID_COLUMN = 'ID' Z_COLUMN = 'Z' X_COLUMN = 'X' Y_COLUMN = 'Y' data = pd.DataFrame({ ID_COLUMN: np.random.choice(['A', 'B', 'C'], 1000), Z_COLUMN: np.random.choice([10, 20, 30], 1000), X_COLUMN: np.random.rand(1000) * 100, Y_COLUMN: np.random.rand(1000) * 50 }) os.makedirs('plots', exist_ok=True) for data_id in data[ID_COLUMN].unique(): filtered_data = data[data[ID_COLUMN] == data_id] for z_value in filtered_data[Z_COLUMN].unique(): filtered_data_z = filtered_data[filtered_data[Z_COLUMN] == z_value] fig = plt.figure() plt.plot(filtered_data_z[X_COLUMN], filtered_data_z[Y_COLUMN]) plt.title(f'ID: {data_id}, Z: {z_value}') plt.xlabel(X_COLUMN) plt.ylabel(Y_COLUMN) plt.savefig(f'plots/ID_{data_id}_Z_{z_value}.png') plt.close(fig) # 第二步:批量审核脚本 import glob import cv2 feedback = [] plot_files = glob.glob('plots/*.png') for file in plot_files: filename = os.path.basename(file) data_id = filename.split('_')[1] z_value = filename.split('_')[3].split('.')[0] img = cv2.imread(file) cv2.imshow(f'ID: {data_id}, Z: {z_value}', img) while True: key = cv2.waitKey(0) if key == ord('y'): feedback.append((data_id, z_value, 'accept')) break elif key == ord('n'): feedback.append((data_id, z_value, 'reject')) break elif key == ord('q'): pd.DataFrame(feedback, columns=['ID', 'Z', 'Feedback']).to_csv('feedback.csv', index=False) cv2.destroyAllWindows() exit() cv2.destroyAllWindows() pd.DataFrame(feedback, columns=['ID', 'Z', 'Feedback']).to_csv('feedback.csv', index=False)
(3)改用独立GUI工具
如果Jupyter环境限制过多,可使用PyQt、Streamlit等工具构建独立审核界面,内存管理更灵活,适合大规模数据处理。比如Streamlit可快速搭建Web界面,支持图表显示和按钮输入,无需复杂GUI开发。
内容的提问来源于stack exchange,提问作者JCV
相关产品推荐
相关产品推荐

