如何将SHAP forceplot通过emit推送到HTML的iframe中展示
问题根因
SHAP生成的force plot HTML源码中自带{{obj}}、{{#each}}这类Mustache模板语法标记,和Flask默认使用的Jinja2模板语法(同样使用{{ }}作为变量占位符)完全冲突。
原有render_template方案正常是因为你给传递的SHAP HTML变量加了|safe过滤器,直接输出转义后的内容,不会触发Jinja2的二次解析;改为SocketIO推送后触发报错,是因为SHAP HTML里的{{obj}}标记被Jinja2识别为模板变量,尝试在当前渲染上下文查找obj变量失败,因此抛出未定义错误。
解决方法
1. 后端预处理SHAP HTML,避免Jinja2解析标记
生成SHAP force plot HTML后,先对特殊标记做转义处理,再通过SocketIO推送:
import shap from jinja2 import escape # 生成SHAP force plot HTML force_plot = shap.force_plot(explainer.expected_value, shap_values[0], X.iloc[0], show=False) shap_html = shap.getjs() + shap.html(force_plot) # 转义双大括号标记,避免被Jinja2识别为模板变量 processed_html = shap_html.replace("{{", r"{{ '{{' }}").replace("}}", r"{{ '}}' }}") # 也可直接用escape函数全量转义 # processed_html = str(escape(shap_html)) # 推送处理后的内容 socketio.emit("update_shap", {"html": processed_html})
2. 前端直接赋值,不要经过Jinja2渲染
前端接收SocketIO推送的内容时,直接用原生JS操作DOM,不要把返回内容放在{{ }}等Jinja2模板标记中:
// iframe赋值方案 socket.on('update_shap', function(data) { const iframe = document.getElementById('shap-iframe'); // 先解码再赋值 iframe.srcdoc = decodeURIComponent(data.html); // 开放必要权限保证SHAP脚本正常执行 iframe.sandbox = "allow-scripts allow-same-origin"; }); // 兜底DIV插入方案(无需iframe) // socket.on('update_shap', function(data) { // document.getElementById('shap-container').innerHTML = decodeURIComponent(data.html); // });
内容的提问来源于stack exchange,提问作者Sade
相关产品推荐
相关产品推荐

