基于DateTimeIndex构建双x轴(月/年)折线柱状组合图问题求助
完整可运行修正代码
# 导入依赖包 import pandas as pd import numpy as np import matplotlib.pyplot as plt # 生成随机测试数据,固定随机种子方便复现 np.random.seed(123) df = pd.DataFrame(np.random.randn(56, 5)) df2 = pd.DataFrame(np.random.randn(56, 3)) # 设置月度DateTimeIndex df.index = pd.date_range('2017-01-01', periods=len(df), freq='MS') df2.index = pd.date_range('2017-01-01', periods=len(df2), freq='MS') # 全局绘图配置 plt.rcParams['font.serif'] = ['Times New Roman'] plt.rc('legend',**{'fontsize':9},frameon=False) plt.style.use('ggplot') fig, ax_bottom = plt.subplots(figsize=(10,8)) # 解决堆叠排序问题:按列平均值降序排列,实现平均值大的类别在堆叠最底层 df = df[df.mean().sort_values(ascending=False).index] # 解决堆叠失效问题:一次性传入所有列绘制堆叠柱状图,避免逐列绘制无法触发堆叠逻辑 df.plot(kind='bar', stacked=True, color=['darkblue', 'blue', 'yellow', 'green', 'gray'], ax=ax_bottom) # 解决X轴对齐问题:折线图使用和柱状图一致的位置索引绘图 x_pos = np.arange(len(df2)) line = ax_bottom.plot(x_pos, df2.iloc[:,0], color='red', linewidth=2, label='折线指标') # 解决折线末尾标签问题:获取最后一个点坐标添加数值标注 last_x = x_pos[-1] last_y = df2.iloc[-1, 0] ax_bottom.annotate(f'{last_y:.1f}', xy=(last_x, last_y), xytext=(5, 0), textcoords='offset points', fontsize=10) # 解决双X轴配置问题 # 上方X轴显示月份,仅保留季度月和最后一个月的标签 ax_top = ax_bottom.twiny() tick_positions = [] tick_labels = [] for i, date in enumerate(df.index): month = date.month if month in [3,6,9,12] or i == len(df.index)-1: tick_positions.append(i) tick_labels.append(str(month)) ax_top.set_xticks(tick_positions) ax_top.set_xticklabels(tick_labels) ax_top.set_xlim(ax_bottom.get_xlim()) # 下方X轴显示年份,仅在每年数据的中间位置显示年份标签 year_ticks = [] year_labels = [] current_year = None year_start_idx = 0 for i, date in enumerate(df.index): if date.year != current_year: if current_year is not None: year_ticks.append((year_start_idx + i -1)/2) year_labels.append(str(current_year)) current_year = date.year year_start_idx = i # 补充最后一个年份标签 year_ticks.append((year_start_idx + len(df.index)-1)/2) year_labels.append(str(current_year)) ax_bottom.set_xticks(year_ticks) ax_bottom.set_xticklabels(year_labels, fontsize=12) # 合并图例 handles1, labels1 = ax_bottom.get_legend_handles_labels() handles2, labels2 = ax_top.get_legend_handles_labels() ax_bottom.legend(handles1 + handles2, labels1 + labels2, loc='upper left') plt.tight_layout() plt.show()
问题对应解决逻辑
- DateTimeIndex下混合绘制柱状图和折线图时,pandas的柱状图默认会把时间索引转换为从0开始的整数位置索引,折线图直接用时间索引绘图会出现刻度不匹配的问题,统一使用位置索引绘制折线是当前场景下最合理的刻度对齐方案。
- 双X轴通过
twiny()方法生成上方X轴实现,遍历时间索引筛选出季度月和序列末尾最后一个月的位置作为上方轴的刻度,设置对应月份标签;下方轴计算每一年数据的中间位置作为刻度,仅显示年份标签即可满足需求。 - 堆叠失效是因为原代码逐列调用柱状图绘制方法,每次调用都是独立绘图操作,无法触发堆叠逻辑,一次性传入所有需要堆叠的列并开启
stacked=True参数即可正常生成堆叠柱状图。 - 堆叠层级排序可通过预计算每列平均值,按平均值降序重排列DataFrame的列顺序后再绘图实现,平均值越大的列会越靠近堆叠底层。
- 折线末尾数值标签可通过获取最后一个数据点的坐标,调用
annotate方法添加标注实现,可通过偏移参数调整标签位置避免和折线重叠。
内容的提问来源于stack exchange,提问作者WX1505
相关产品推荐
相关产品推荐

