
本文详解如何在保持 30 分钟数据分辨率的前提下,仅显示整点(如 00:00、01:00…23:00)作为 x 轴刻度,并确保这些刻度严格等距、从图表最左端延伸至最右端,彻底消除首尾空白。
本文详解如何在保持 30 分钟数据分辨率的前提下,仅显示整点(如 00:00、01:00…23:00)作为 x 轴刻度,并确保这些刻度严格等距、从图表最左端延伸至最右端,彻底消除首尾空白。
在使用 matplotlib 绘制时间序列图时,一个常见需求是:数据采样频率高(如每 30 分钟一条),但只需在 X 轴上展示关键时间点(如每小时整点),且要求这些刻度均匀分布、覆盖整个横轴可视范围(即首刻度对齐左边界,末刻度对齐右边界)。直接调用 plt.xticks() 设置固定标签(如 [“00:00”, “01:00”, …, “23:00”])往往会导致刻度挤在中间、两端留白严重——这是因为 Matplotlib 默认将刻度位置映射到数据索引(0, 1, 2, …, 47),而整点实际只出现在偶数索引(0, 2, 4, …, 46),共 24 个点;若强行指定 24 个标签却不显式指定对应的位置坐标,系统无法自动拉伸分布。
✅ 正确解法是:先让 Matplotlib 自动完成初始绘图与刻度生成,再基于其生成的原始刻度标签动态筛选出整点位置,并重设 xticks 为等间距的数值序列(0, 2, 4, …, 46),最后同步更新标签。这样既保留了数据完整性,又实现了视觉上的均匀铺展。
以下是推荐的完整实现方案(适配您原始代码):
import pandas as pd import matplotlib.pyplot as plt file_path = r"C:Userswi9632DesktopResults_End_MultiOptLoad_Profile_FigureCombined_Table.csv" df = pd.read_csv(file_path, sep=';', encoding='latin-1') methods = { "Dichotomous Method [kW]": ("Dichotomous Method", "tab:orange"), "Conventional Control [kW]": ("Conventional Control", "tab:purple"), "NSGA-II [kW]": ("NSGA-II", "tab:cyan"), "SPEA-II [kW]": ("SPEA-II", "gold"), "PALSS [kW]": ("PALSS", "red"), "RELAPALLS [kW]": ("RELAPALLS", "lawngreen") } fig, axes = plt.subplots(6, 1, figsize=(12, 20), sharex=True) # 绘制第一行:6 种方法对比 for method, (name, color) in methods.items(): axes[0].plot(df["time of day"], df[method], label=name, color=color) axes[0].set_ylabel("Power [kW]") axes[0].set_title("Power Profiles for Different Methods") axes[0].legend(loc='upper center', bbox_to_anchor=(0.5, 1.15), ncol=3) # 绘制其余子图 variables = ["Space Heating [kW]", "DHW [kW]", "Availability of the EV", "Outside Temperature [°C]", "Price [Cent/kWh]"] for i, variable in enumerate(variables, start=1): axes[i].plot(df["time of day"], df[variable], color='blue') axes[i].set_ylabel(variable) # ? 关键步骤:精简并均匀分布 X 轴刻度(整点) # 1. 先触发绘图以生成默认刻度(重要!否则 get_xticklabels() 返回空) plt.tight_layout() # 2. 获取底部共享 X 轴的最后一个子图(或任一共享轴的 axes[i])的刻度标签 ax_bottom = axes[-1] original_labels = [lbl.get_text() for lbl in ax_bottom.get_xticklabels()] # 3. 找出所有以 ":00" 结尾的时间字符串对应的原始索引位置(即 0, 2, 4, ..., 46) hour_indices = [i for i, lbl in enumerate(original_labels) if lbl.endswith(':00')] # 4. 构造等间距的刻度位置:从 0 到 len(df)-1 均匀取 24 个点(对应 24 小时) # 注意:df 有 48 行(0–47),整点位于索引 0,2,4,...,46 → 共 24 个位置 tick_positions = list(range(0, len(df), 2)) # [0, 2, 4, ..., 46] tick_labels = [f"{h:02d}:00" for h in range(24)] # ["00:00", "01:00", ..., "23:00"] # 5. 对所有共享 X 轴的子图统一设置刻度 for ax in axes: ax.set_xticks(tick_positions) ax.set_xticklabels(tick_labels, rotation=0, ha='center') # 水平居中更清晰 plt.xlabel("Time of Day") plt.tight_layout() # 可选:进一步压缩边距(消除残余空白) plt.subplots_adjust(bottom=0.05, top=0.95) combined_output_path = r"C:Userswi9632DesktopResults_End_MultiOptLoad_Profile_FigureParetoFronts_combined.png" plt.savefig(combined_output_path, dpi=200, bbox_inches='tight') plt.show()
? 关键说明与注意事项:
-
为什么必须先 tight_layout() 再获取标签?
get_xticklabels() 依赖于已渲染的布局信息。若在绘图未完成前调用,可能返回空列表或错误索引。务必在 plot() 后、set_xticks() 前执行一次 tight_layout() 或 draw()。 -
range(0, len(df), 2) 是核心:
数据共 48 行(00:00 到 23:30),整点严格对应索引 0, 2, 4, …, 46。该表达式精确生成这 24 个位置,确保刻度物理间距相等,自然铺满横轴。 -
避免使用字符串标签直接设 xticks:
错误写法 plt.xticks([“00:00″,”01:00”,…]) 会丢失位置映射,导致刻度堆积。必须同时指定数值位置和对应标签。 -
旋转与对齐优化:
rotation=0 + ha=’center’ 保证时间标签水平居中对齐刻度线,提升可读性;若空间紧张,可改用 rotation=30 并调整 plt.subplots_adjust()。 -
扩展性提示:
若未来数据频率变化(如 15 分钟/条),只需将 range(0, len(df), 2) 改为 range(0, len(df), 4) 即可适配整点(每 4 行一个整点)。
通过上述方法,您将获得专业级的时间轴呈现效果:X 轴刻度精准对应每小时整点,视觉上均匀延展至图表左右边界,彻底解决首尾空白与刻度偏移问题,同时完全兼容高分辨率原始数据。