如何在Plotly go.Box中分组数据并指定10/90分位数
问题:Plotly分组箱线图自定义须线分位数失败
我想用Plotly制作分组箱线图,将默认展示最小值/最大值的须线改为展示第10和第90分位数,但尝试后未成功实现。以下是我的代码及统计数据,目前箱线图未正确应用指定分位数,求协助解决。
import plotly.graph_objects as go from itertools import cycle import numpy as np import pandas as pd # 生成数据框 num_rows = 100 ids = np.random.randint(1, 1000, size=num_rows) categories = np.random.choice(['A', 'B', 'C', 'D'], size=num_rows) phases, durations = [], [] for id in ids: phases.extend([1, 2, 3]) durations.extend(np.random.randint(100, 1001, size=3)) data = { 'id': np.repeat(ids, 3), 'category': np.repeat(categories, 3), 'phase': phases, 'duration': durations} df = pd.DataFrame(data) df = df.sample(frac=1).reset_index(drop=True) # 计算统计量 p10 = lambda x: x.quantile(0.10) p25 = lambda x: x.quantile(0.25) p50 = lambda x: x.quantile(0.50) p75 = lambda x: x.quantile(0.75) p90 = lambda x: x.quantile(0.90) to_display = df.groupby(['phase', 'category'], as_index=False).agg( p_10=('duration', p10), p_25=('duration', p25), p_75=('duration', p75), median=('duration', p50), p_90=('duration', p90), avg=('duration', 'mean') ) # 创建图表 palette = cycle(['black', 'grey', 'red', 'blue']) fig_grouped = go.Figure() for i, cat in enumerate(df['category'].unique()): df_plot = df[df['category']==cat] fig_grouped.add_trace(go.Box(y=df_plot['duration'], x=df_plot['phase'], name=cat, boxpoints=False, marker_color=next(palette))) fig_grouped.update_traces(boxmean=True) fig_grouped.update_layout(boxmode='group') temp = to_display[to_display['category']==cat] q1 = list(temp['p_25'].values) median = list(temp['median'].values) q3 = list(temp['p_75'].values) lowerfence = list(temp['p_10'].values) upperfence = list(temp['p_90'].values) avg = list(temp['avg'].values) print(cat) print('q1', q1) print('median', median) print('q3', q3) print('lowerfence', lowerfence) print('upperfence', upperfence) print('avg', avg) fig.update_traces(q1=q1, median=median, q3=q3, lowerfence=lowerfence, upperfence=upperfence ) fig_grouped.show()
预计算统计数据(to_display数据框)
phase category p_10 p_25 p_75 p_90 avg 0 1 A 231.8 376.25 803.75 920.6 591.964286 1 1 B 124.0 200.50 669.50 886.4 468.913043 2 1 C 203.2 318.50 784.50 901.6 535.739130 3 1 D 175.0 294.75 821.00 882.5 528.961538 4 2 A 326.5 448.25 764.25 842.8 586.928571 5 2 B 169.0 321.50 825.50 933.6 599.304348 6 2 C 138.8 352.50 808.50 936.2 556.260870 7 2 D 166.5 376.50 783.50 872.0 590.961538 8 3 A 260.2 419.00 707.00 828.2 564.928571 9 3 B 190.0 528.00 836.50 962.0 614.043478 10 3 C 343.4 450.00 812.00 894.6 630.652174 11 3 D 204.5 364.00 833.25 929.0 604.384615
修复后的代码
import plotly.graph_objects as go from itertools import cycle import numpy as np import pandas as pd # 生成数据框 num_rows = 100 ids = np.random.randint(1, 1000, size=num_rows) categories = np.random.choice(['A', 'B', 'C', 'D'], size=num_rows) phases, durations = [], [] for id in ids: phases.extend([1, 2, 3]) durations.extend(np.random.randint(100, 1001, size=3)) data = { 'id': np.repeat(ids, 3), 'category': np.repeat(categories, 3), 'phase': phases, 'duration': durations} df = pd.DataFrame(data) df = df.sample(frac=1).reset_index(drop=True) # 计算统计量 p10 = lambda x: x.quantile(0.10) p25 = lambda x: x.quantile(0.25) p50 = lambda x: x.quantile(0.50) p75 = lambda x: x.quantile(0.75) p90 = lambda x: x.quantile(0.90) to_display = df.groupby(['phase', 'category'], as_index=False).agg( p_10=('duration', p10), p_25=('duration', p25), p_75=('duration', p75), median=('duration', p50), p_90=('duration', p90), avg=('duration', 'mean') ) # 创建图表 palette = cycle(['black', 'grey', 'red', 'blue']) fig_grouped = go.Figure() # 按分类添加自定义统计量的箱线图 for cat in df['category'].unique(): temp = to_display[to_display['category'] == cat].sort_values('phase') fig_grouped.add_trace(go.Box( x=temp['phase'], name=cat, boxpoints=False, marker_color=next(palette), # 直接传入预计算的统计值 q1=temp['p_25'].tolist(), median=temp['median'].tolist(), q3=temp['p_75'].tolist(), lowerfence=temp['p_10'].tolist(), upperfence=temp['p_90'].tolist(), mean=temp['avg'].tolist(), boxmean=True )) fig_grouped.update_layout( boxmode='group', title='分组箱线图(须线为10/90分位数)', xaxis_title='阶段', yaxis_title='时长' ) fig_grouped.show()
修复说明
- 修正变量名错误:原代码误用
fig.update_traces,实际图表变量为fig_grouped,修复后直接在添加trace时传入统计量,避免批量修改出错 - 精准绑定统计值:不再传入原始数据,直接使用预计算的分位数、中位数和均值,确保箱线图完全遵循自定义规则
- 保证顺序一致性:对每个分类的统计数据按
phase排序,确保x轴阶段顺序正确 - 简化逻辑:将
boxmean=True直接嵌入trace参数,减少冗余操作
内容的提问来源于stack exchange,提问作者Newbielp
相关产品推荐
相关产品推荐

