使用pandas groupby实现带置信区间的Plotly分组折线图对比多场景
实现方案
你需要手动遍历所有分组组合为每个组添加置信区间填充层,两种可视化库均可实现,具体实现代码如下:
Plotly 实现方案
核心思路是用make_subplots创建分面网格,遍历second_factor(分面维度)和first_factor(颜色分组维度)的所有组合,为每个组合依次添加置信区间填充和均值线:
import plotly.graph_objects as go from plotly.subplots import make_subplots # 提取唯一分组值 first_factors = reset_df['first_factor'].unique() second_factors = reset_df['second_factor'].unique() # 自定义分组颜色,长度与first_factor数量一致即可 color_palette = ['rgb(31, 119, 255)', 'rgb(255, 127, 14)', 'rgb(44, 160, 44)', 'rgb(214, 39, 40)'] # 创建1行N列的分面图,共享Y轴 fig = make_subplots( rows=1, cols=len(second_factors), subplot_titles=[f'second_factor={s}' for s in second_factors], shared_yaxes=True ) for col_idx, sec_factor in enumerate(second_factors, 1): for color_idx, first_factor in enumerate(first_factors): # 筛选当前分组的时序数据 filter_df = reset_df[ (reset_df['first_factor'] == first_factor) & (reset_df['second_factor'] == sec_factor) ].sort_values('tick') curr_color = color_palette[color_idx] fill_color = curr_color.replace('rgb', 'rgba').replace(')', ',0.3)') # 添加上置信区间边界(透明线,仅做填充锚点) fig.add_trace(go.Scatter( x=filter_df['tick'], y=filter_df['ci95_hi'], line=dict(width=0, color=curr_color), mode='lines', showlegend=False, hoverinfo='skip' ), row=1, col=col_idx) # 添加下置信区间边界 + 填充到上边界 fig.add_trace(go.Scatter( x=filter_df['tick'], y=filter_df['ci95_lo'], line=dict(width=0, color=curr_color), mode='lines', fill='tonexty', fillcolor=fill_color, name=f'95%CI {first_factor}', showlegend=col_idx==1 # 仅第一个分面显示图例避免重复 ), row=1, col=col_idx) # 添加均值线 fig.add_trace(go.Scatter( x=filter_df['tick'], y=filter_df['mean'], line=dict(width=2, color=curr_color), mode='lines', name=f'均值 {first_factor}', showlegend=col_idx==1 ), row=1, col=col_idx) # 全局布局配置 fig.update_layout( title='多场景时序均值与95%置信区间', xaxis_title='Tick', yaxis_title='数值', hovermode='x' ) fig.show()
如果不需要分面,仅在单图中区分所有分组,删除分面逻辑、给不同second_factor组合配置不同线型即可。
Bokeh 实现方案
Bokeh可以用varea接口直接绘制填充区间,配合网格布局实现分面效果:
from bokeh.plotting import figure, show from bokeh.layouts import gridplot from bokeh.palettes import Category10 first_factors = reset_df['first_factor'].unique() second_factors = reset_df['second_factor'].unique() color_palette = Category10[len(first_factors)] if len(first_factors) <=10 else Category20[len(first_factors)] plot_list = [] for sec_factor in second_factors: p = figure(title=f'second_factor={sec_factor}', x_axis_label='Tick', y_axis_label='数值') for color_idx, first_factor in enumerate(first_factors): filter_df = reset_df[ (reset_df['first_factor'] == first_factor) & (reset_df['second_factor'] == sec_factor) ].sort_values('tick') curr_color = color_palette[color_idx] # 绘制置信区间填充 p.varea( x=filter_df['tick'], y1=filter_df['ci95_lo'], y2=filter_df['ci95_hi'], alpha=0.3, color=curr_color, legend_label=f'95%CI {first_factor}' ) # 绘制均值线 p.line( x=filter_df['tick'], y=filter_df['mean'], line_width=2, color=curr_color, legend_label=f'均值 {first_factor}' ) plot_list.append(p) # 横向排列所有分面 show(gridplot([plot_list], width=450, height=400))
内容的提问来源于stack exchange,提问作者FrostyFrog
相关产品推荐
相关产品推荐

