You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中如何用Altair实现带年份的双层X轴折线图?

用Altair实现双层X轴(年份+月份)的NPS趋势图

问题背景

我有一个包含Year(int64)、Month(字符串)、Company(字符串)、NPS(int64)列的DataFrame,想要展示各公司NPS随年月的变化,但无法用Altair实现双层X轴(年份、月份分层)的效果。

示例数据

YearMonthCompanyNPS
2019JanCompetitor A29
2019FebCompetitor B29
2019MarCompetitor C31
............

尝试代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 05:45:34