如何用Plotly高效绘制线段?优化单线段单Trace的实现方式
Plotly 线段绘制优化:避免单线段对应独立Trace
现有代码能实现线段绘制,但每条线段都创建独立的Scatter Trace,当数据量大时会导致性能下降,且不够高效。如何优化实现,减少Trace数量?
原代码如下:
import pandas as pd import numpy as np import plotly.graph_objects as go from plotly.subplots import make_subplots def plot_segments(df): xname = "ts" yname = "duration" dfg = df.groupby('name') fig = go.Figure() colors=['#4f81bd','#c0504d','#9bbb59','#8064a2','#4bacc6','#f79646','#0000ff'] traces = [] dy = 1.1 for i,[gname, df] in enumerate(dfg): for index,row in df.iterrows(): x1 = row['ts'] x2 = x1 + pd.to_timedelta(row['duration'],unit = 's') x = [x1,x2] y = [dy,dy] trace1 = go.Scatter( x=x, y=y, mode='lines+markers', marker=dict( size=4, line=dict(width=0,color=colors[i])), line=dict(width=1,color=colors[i]), ) traces.append(trace1) dy += .03 fig.add_traces(traces) fontsize = 10 fig.add_annotation(dict( font=dict(color="black",size=fontsize), x=0.5, xshift=0, y=0, yshift=-30, showarrow=False, text='Timestamp', textangle=0, xref="paper", yref="paper", xanchor='center', yanchor='top', )) fig.add_annotation(dict( font=dict(color="black",size=fontsize), x=-0, xshift=-20, y=0.5, showarrow=False, text='Category', textangle=-90, xref="paper", yref="paper", xanchor='right', yanchor='middle', )) xpading=.05 fig.update_layout( margin=dict(l=50,t=40,r=10,b=40), plot_bgcolor='#ffffff',#'rgb(12,163,135)', paper_bgcolor='#ffffff', title="Segments", #xaxis2_title="Timestamp", #yaxis_title="Interval(secs)", title_x=0.5, showlegend=False, legend=dict(x=.02,y=1.05), barmode='group', bargap=0.05, bargroupgap=0.0, font=dict( family="Courier New, monospace", size=fontsize, color="black" ), xaxis=dict( visible=True, tickangle=-15, tickformat = '%m-%d %H:%M:%S',#datetime format showline=True, linecolor='black', color='black', linewidth=.5, ticks='outside', showgrid=False, gridcolor='grey', gridwidth=.5, griddash='solid',#'dot', ), yaxis=dict( range=[0,1.2], showline=True, linecolor='black', color='black', linewidth=.5, showgrid=True, gridcolor='grey', gridwidth=.5, griddash='solid',#'dot', zeroline=True, zerolinecolor='grey', zerolinewidth=.5, showticklabels=True, ), ) fig.show() return data = [ ['04-21 20:54:10.247','A',2], ['04-21 20:54:15.247','A',1], ['04-21 20:54:20.247','A',2], ['04-21 20:54:25.247','A',1], ['04-21 20:54:11.247','B',2], ['04-21 20:54:26.247','B',1], ['04-21 20:54:31.247','B',2], ['04-21 20:54:36.247','B',1] ] df = pd.DataFrame(data,columns=['ts','name','duration']) df['ts'] = pd.to_datetime(df['ts'],format="%m-%d %H:%M:%S.%f") plot_segments(df)
原效果:
优化方案:同一类别共用一个Trace
核心思路:同一分类(name)的所有线段合并到同一个Scatter Trace中,通过在不同线段之间插入None实现线段分隔,Plotly会自动识别None并断开线段,这样每个分类仅需一个Trace,大幅减少Trace数量,提升渲染性能。
优化后的代码:
import pandas as pd import numpy as np import plotly.graph_objects as go def plot_segments_optimized(df): xname = "ts" yname = "duration" dfg = df.groupby('name') fig = go.Figure() colors=['#4f81bd','#c0504d','#9bbb59','#8064a2','#4bacc6','#f79646','#0000ff'] dy = 1.1 for i, (gname, df_group) in enumerate(dfg): x_vals = [] y_vals = [] for _, row in df_group.iterrows(): x1 = row['ts'] x2 = x1 + pd.to_timedelta(row['duration'], unit='s') # 添加当前线段的两个端点 x_vals.extend([x1, x2]) y_vals.extend([dy, dy]) # 插入None分隔不同线段 x_vals.append(None) y_vals.append(None) # 移除最后多余的None(可选,Plotly也可兼容) if x_vals: x_vals.pop() y_vals.pop() # 为当前类别创建单个Trace trace = go.Scatter( x=x_vals, y=y_vals, mode='lines+markers', marker=dict( size=4, line=dict(width=0, color=colors[i])), line=dict(width=1, color=colors[i]), name=gname # 可选,用于图例显示类别 ) fig.add_trace(trace) dy += .03 fontsize = 10 fig.add_annotation(dict( font=dict(color="black", size=fontsize), x=0.5, xshift=0, y=0, yshift=-30, showarrow=False, text='Timestamp', textangle=0, xref="paper", yref="paper", xanchor='center', yanchor='top', )) fig.add_annotation(dict( font=dict(color="black", size=fontsize), x=0, xshift=-20, y=0.5, showarrow=False, text='Category', textangle=-90, xref="paper", yref="paper", xanchor='right', yanchor='middle', )) fig.update_layout( margin=dict(l=50, t=40, r=10, b=40), plot_bgcolor='#ffffff', paper_bgcolor='#ffffff', title="Segments (Optimized)", title_x=0.5, showlegend=True, # 可选,开启图例查看类别 legend=dict(x=.02, y=1.05), font=dict( family="Courier New, monospace", size=fontsize, color="black" ), xaxis=dict( visible=True, tickangle=-15, tickformat='%m-%d %H:%M:%S', showline=True, linecolor='black', color='black', linewidth=.5, ticks='outside', showgrid=False, gridcolor='grey', gridwidth=.5, griddash='solid', ), yaxis=dict( range=[0, 1.2], showline=True, linecolor='black', color='black', linewidth=.5, showgrid=True, gridcolor='grey', gridwidth=.5, griddash='solid', zeroline=True, zerolinecolor='grey', zerolinewidth=.5, showticklabels=True, ), ) fig.show() return data = [ ['04-21 20:54:10.247','A',2], ['04-21 20:54:15.247','A',1], ['04-21 20:54:20.247','A',2], ['04-21 20:54:25.247','A',1], ['04-21 20:54:11.247','B',2], ['04-21 20:54:26.247','B',1], ['04-21 20:54:31.247','B',2], ['04-21 20:54:36.247','B',1] ] df = pd.DataFrame(data,columns=['ts','name','duration']) df['ts'] = pd.to_datetime(df['ts'],format="%m-%d %H:%M:%S.%f") plot_segments_optimized(df)
优化说明
- 性能提升:原代码8条线段对应8个Trace,优化后2个类别对应2个Trace,数据量越大,渲染效率提升越明显。
- 视觉一致性:通过
None分隔线段,最终呈现的视觉效果与原代码完全一致。 - 可扩展性:新增类别时仅需补充对应颜色,核心逻辑无需修改,维护成本更低。
内容的提问来源于stack exchange,提问作者lucky1928
相关产品推荐
相关产品推荐

