如何用Plotly Express实现多类别同Y轴散点及堆叠子图?
问题描述
我用Plotly绘制散点图,X轴为时间字段DATE,Y轴为分类变量CODE,按SOURCE字段上色。目前遇到两个问题:
- 如何让不同
SOURCE的点显示在同一个自定义Y轴上(当前并非同一Y轴); - 如何将每个
CODE类别放在独立图表中并堆叠展示?
样本数据
| ID | DATE | CODE | SOURCE |
|---|---|---|---|
| P04 | 2016-08-08 | f | m1 |
| P04 | 2015-05-08 | f | m1 |
| P04 | 2010-07-20 | v | m3 |
| P04 | 2013-12-06 | g | m4 |
| P08 | 2018-03-01 | h | m4 |
当前代码
def char(): color_discrete_map = {'df1': 'rgb(255,0,0)', 'df2': 'rgb(0,255,0)', 'df3': '#11FCE4', 'df4': '#9999FF', 'df5': '#606060', 'df6': '#CC6600'} fig = px.scatter(df, x='DATE', y='CODE', color='SOURCE', width=800, height=500, color_discrete_map=color_discrete_map) fig.update_layout(xaxis_type='category') fig.update_layout( margin=dict(l=250, r=0, t=0, b=20), ) fig.update_layout(xaxis=dict(tickformat="%y-%m")) fig.update_xaxes(ticks= "outside", ticklabelmode= "period", tickcolor= "black", ticklen=10, minor=dict( ticklen=4, dtick=7*24*60*60*1000, tick0="2016-07-03", griddash='dot', gridcolor='white') ) st.plotly_chart(fig)
效果对比
期望效果

当前实现效果

解决方案
问题1:让不同SOURCE共享同一Y轴
当前不同SOURCE的点不在同一Y轴,是因为CODE分类在不同SOURCE中无交叉,导致Plotly自动分割Y轴范围。可通过强制统一Y轴分类顺序解决:
- 提取所有
CODE唯一值并自定义排序:
code_categories = sorted(df['CODE'].unique())
- 绘图时通过
category_orders参数强制Y轴分类顺序,确保所有点映射到同一组刻度:
fig = px.scatter(df, x='DATE', y='CODE', color='SOURCE', width=800, height=500, color_discrete_map=color_discrete_map, category_orders={'CODE': code_categories})
- 修正X轴类型:
DATE是时间字段,应设置为'date'而非'category',避免日期被当作独立分类:
fig.update_layout(xaxis_type='date')
问题2:每个CODE类别独立图表并堆叠展示
使用Plotly的make_subplots创建垂直堆叠子图,每个子图对应一个CODE类别:
- 导入子图工具:
from plotly.subplots import make_subplots
- 创建堆叠子图布局,共享X轴:
code_list = sorted(df['CODE'].unique()) fig = make_subplots(rows=len(code_list), cols=1, shared_xaxes=True, vertical_spacing=0.05, subplot_titles=[f'CODE: {code}' for code in code_list])
- 遍历每个
CODE,将对应数据点添加到子图:
# 匹配SOURCE的颜色,根据实际值调整 color_map = {'m1': 'rgb(255,0,0)', 'm3': '#11FCE4', 'm4': '#9999FF'} for idx, code in enumerate(code_list, 1): subset = df[df['CODE'] == code] for source in subset['SOURCE'].unique(): source_subset = subset[subset['SOURCE'] == source] fig.add_scatter(x=source_subset['DATE'], y=[code]*len(source_subset), mode='markers', marker_color=color_map.get(source, '#606060'), name=f'SOURCE: {source}', row=idx, col=1) # 调整布局样式 fig.update_layout(height=300*len(code_list), width=800, margin=dict(l=250, r=0, t=50, b=20)) fig.update_xaxes(tickformat="%y-%m", ticks="outside", tickcolor="black", ticklen=10, minor=dict(ticklen=4, dtick=7*24*60*60*1000, tick0="2016-07-03", griddash='dot', gridcolor='white')) fig.update_yaxes(showticklabels=False) # 子图标题已标注CODE,可隐藏Y轴刻度
- 用Streamlit展示:
st.plotly_chart(fig)
内容的提问来源于stack exchange,提问作者user14269252
相关产品推荐
相关产品推荐

