Python中如何用Altair实现带年份的双层X轴折线图?
用Altair实现双层X轴(年份+月份)的NPS趋势图
问题背景
我有一个包含Year(int64)、Month(字符串)、Company(字符串)、NPS(int64)列的DataFrame,想要展示各公司NPS随年月的变化,但无法用Altair实现双层X轴(年份、月份分层)的效果。
示例数据
| Year | Month | Company | NPS |
|---|---|---|---|
| 2019 | Jan | Competitor A | 29 |
| 2019 | Feb | Competitor B | 29 |
| 2019 | Mar | Competitor C | 31 |
| ... | ... | ... | ... |
尝试代码
chart = alt.Chart(df).mark_line().encode( x=alt.X('Month:N'), y=alt.Y('NPS:Q', scale=alt.Scale(domain=[0,60])), color=alt.Color('company:N', scale=alt.Scale(domain=['Our Company', 'Competitor A', 'Competitor B', 'Competitor C', 'Competitor D'], range=['#000', '#c8c8c8', '#c8c8c8', '#c8c8c8', '#c8c8c8'])) )
当前输出

目标效果

解决方案
Altair原生不支持双层轴,但可以通过分层图表叠加的方式模拟实现双层X轴效果,步骤如下:
1. 确保月份有序
因为Month是字符串类型,需要先转换为有序分类,避免X轴月份顺序混乱:
import pandas as pd import altair as alt # 定义月份顺序 month_order = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] df['Month'] = pd.Categorical(df['Month'], categories=month_order, ordered=True)
2. 创建主趋势线图表
主图表以月份为X轴,保留年份信息用于后续分组:
line_chart = alt.Chart(df).mark_line().encode( x=alt.X('Month:N', title=None, axis=alt.Axis(labelAngle=0)), y=alt.Y('NPS:Q', scale=alt.Scale(domain=[0,60]), title='NPS'), color=alt.Color('Company:N', scale=alt.Scale(domain=['Our Company', 'Competitor A', 'Competitor B', 'Competitor C', 'Competitor D'], range=['#000', '#c8c8c8', '#c8c8c8', '#c8c8c8', '#c8c8c8'])) )
3. 添加年份标签层
计算每个年份对应的月份起始位置,将年份文本放置在月份轴上方:
# 获取每个年份的第一个月份,计算标签X轴位置(居中对齐) year_positions = df.groupby('Year')['Month'].first().reset_index() year_positions['x_pos'] = year_positions['Month'].cat.codes + 0.5 # 创建年份标签图表 year_labels = alt.Chart(year_positions).mark_text( fontSize=12, fontWeight='bold', dy=-15 # dy控制标签向上偏移距离 ).encode( x=alt.X('x_pos:Q', axis=None), text='Year:N' )
4. 添加年份分隔线(可选)
在不同年份之间添加垂直分隔线,增强分层视觉效果:
# 获取每个年份的最后一个月份,计算分隔线位置 year_dividers = df.groupby('Year')['Month'].last().reset_index() year_dividers['x_pos'] = year_dividers['Month'].cat.codes + 1 # 创建分隔线图表 divider_lines = alt.Chart(year_dividers).mark_rule( stroke='#ccc', strokeWidth=1 ).encode( x=alt.X('x_pos:Q', axis=None) )
5. 合并所有图层
将趋势线、分隔线、年份标签合并为最终图表:
final_chart = (line_chart + divider_lines + year_labels).properties( width=800, height=400, title='各公司NPS随年月变化趋势' ) final_chart.show()
说明
- 这种方式通过图层叠加模拟了双层X轴的视觉效果,年份标签位于月份轴上方,分隔线清晰区分年份区间
- 可根据实际数据调整标签偏移量、分隔线样式、图表尺寸等参数
- 务必保证月份为有序分类,否则X轴月份顺序会混乱
内容的提问来源于stack exchange,提问作者ku11
相关产品推荐
相关产品推荐

