Jupyter Notebook转HTML后Plotly图表数据丢失问题求助
解决Plotly多图表导出HTML后早期图表数据丢失的问题
问题原因
浏览器对同时活跃的WebGL上下文数量有严格限制(通常在16-24个之间)。你的代码生成了10个独立的Plotly折线图,保存为HTML后所有图表会同时渲染,当数量超过浏览器上限时,最早创建的WebGL上下文会被强制回收,导致对应的图表数据丢失,这就是控制台出现Too many active WebGL contexts警告的原因。
解决方案
1. 切换到SVG静态渲染模式
Plotly默认使用WebGL渲染交互式图表,改用SVG渲染可以完全避免WebGL上下文的限制,同时保持基本的交互功能(悬停、缩放等)。修改代码中图表的渲染方式即可:
import pandas as pd import numpy as np from datetime import datetime, timedelta import plotly.express as px # 创建数据帧 start_date = datetime(2018, 1, 1) end_date = datetime(2022, 12, 31) num_days = (end_date - start_date).days + 1 num_cols = 100 data = np.random.rand(num_days, num_cols) dates = [start_date + timedelta(days=i) for i in range(num_days)] df = pd.DataFrame(data, columns=[f"Column_{i+1}" for i in range(num_cols)]) df['Date'] = dates x = 10 while x < len(df.columns): columns_to_plot = df.columns[x-10:x].tolist() fig = px.line(df, x=df['Date'], y=columns_to_plot) # 使用SVG渲染器替代默认WebGL fig.show(renderer="svg") x += 10
如果需要批量导出HTML,也可以在保存时指定SVG格式:
# 保存单个图表为SVG模式的HTML fig.write_html("chart.html", full_html=True, include_plotlyjs='cdn', renderer="svg")
2. 合并图表减少WebGL上下文数量
将多个折线图合并到同一个画布的子图中,大幅减少独立图表的数量,从而控制WebGL上下文的使用。示例代码如下:
import pandas as pd import numpy as np from datetime import datetime, timedelta import plotly.subplots as sp import plotly.graph_objects as go # 创建数据帧(同之前的代码) start_date = datetime(2018, 1, 1) end_date = datetime(2022, 12, 31) num_days = (end_date - start_date).days + 1 num_cols = 100 data = np.random.rand(num_days, num_cols) dates = [start_date + timedelta(days=i) for i in range(num_days)] df = pd.DataFrame(data, columns=[f"Column_{i+1}" for i in range(num_cols)]) df['Date'] = dates # 创建5行2列的子图(对应10个图表分组) fig = sp.make_subplots(rows=5, cols=2, subplot_titles=[f"Group {i+1}" for i in range(10)]) row, col = 1, 1 x = 10 while x < len(df.columns): columns_to_plot = df.columns[x-10:x].tolist() for col_name in columns_to_plot: fig.add_trace(go.Scatter(x=df['Date'], y=df[col_name], name=col_name), row=row, col=col) # 切换子图位置 col = 2 if col == 1 else 1 if col == 1: row += 1 x += 10 fig.update_layout(height=1500, width=1200, title_text="合并后的多组折线图") fig.show()
这种方式只需要1个WebGL上下文,完全不会触发数量限制,同时保留所有交互功能。
3. 手动清理旧图表的WebGL上下文
如果你需要保留独立图表的布局,可以在渲染新图表前手动销毁旧图表的WebGL上下文。通过Plotly.purge()方法清理指定图表的DOM元素:
import pandas as pd import numpy as np from datetime import datetime, timedelta import plotly.express as px from IPython.display import display, HTML import plotly.io as pio # 创建数据帧(同之前的代码) start_date = datetime(2018, 1, 1) end_date = datetime(2022, 12, 31) num_days = (end_date - start_date).days + 1 num_cols = 100 data = np.random.rand(num_days, num_cols) dates = [start_date + timedelta(days=i) for i in range(num_days)] df = pd.DataFrame(data, columns=[f"Column_{i+1}" for i in range(num_cols)]) df['Date'] = dates # 保存所有图表的HTML字符串 chart_htmls = [] x = 10 while x < len(df.columns): columns_to_plot = df.columns[x-10:x].tolist() fig = px.line(df, x=df['Date'], y=columns_to_plot) chart_htmls.append(pio.to_html(fig, full_html=False)) x += 10 # 生成带清理逻辑的HTML,每次只渲染一个图表,切换时清理前一个 html_content = """ <script src="https://cdn.plot.ly/plotly-latest.min.js"></script> <div id="chart-container"></div> <button id="prev-btn">上一张</button> <button id="next-btn">下一张</button> <script> const charts = [""" + ",".join([f"`{html}`" for html in chart_htmls]) + """]; let currentIndex = 0; function renderChart(index) { // 清理当前容器中的旧图表 Plotly.purge('chart-container'); // 渲染新图表 document.getElementById('chart-container').innerHTML = charts[index]; } renderChart(currentIndex); document.getElementById('prev-btn').addEventListener('click', () => { if (currentIndex > 0) { currentIndex--; renderChart(currentIndex); } }); document.getElementById('next-btn').addEventListener('click', () => { if (currentIndex < charts.length - 1) { currentIndex++; renderChart(currentIndex); } }); </script> """ display(HTML(html_content))
这种方式通过分页加载图表,同一时间只存在一个WebGL上下文,彻底解决数量超限问题。
内容的提问来源于stack exchange,提问作者Marvy
相关产品推荐
相关产品推荐

