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

Plotly双Y轴子图:固定范围、零点对齐及数据边框间距实现

解决方案:带双Y轴、固定范围且零点对齐的子图设置

核心思路

要同时满足固定Y轴范围、主次Y轴零点对齐、数据不贴边框三个要求,关键是基于零点对称扩展范围并保持主次轴比例一致,也可以通过调整轴层级让数据显示在边框前方来优化可读性。

1. 对称扩展范围(兼顾零点对齐与间距)

先计算主/次轴数据离零点最远的绝对值,给这个值加固定余量(比如10%)作为轴范围边界,再通过比例关系推导次轴范围,既能保证零点对齐,又能让数据和边框留足空间。

以Matplotlib为例的代码实现:

import matplotlib.pyplot as plt
import numpy as np

# 模拟数据
x = np.linspace(0, 10, 100)
y1 = 3 * np.sin(x)  # 主Y轴数据
y2 = 0.5 * np.cos(x) * 10  # 次Y轴数据

# 计算主/次轴极值
y1_min, y1_max = y1.min(), y1.max()
y2_min, y2_max = y2.min(), y2.max()

# 找离零点最远的绝对值,计算带余量的范围
max_abs_y1 = max(abs(y1_min), abs(y1_max))
max_abs_y2 = max(abs(y2_min), abs(y2_max))
scale = max_abs_y2 / max_abs_y1  # 主次轴比例,保证零点对齐

margin = 0.1 * max_abs_y1  # 10%的余量
y1_range = [-max_abs_y1 - margin, max_abs_y1 + margin]
y2_range = [-scale*(max_abs_y1 + margin), scale*(max_abs_y1 + margin)]

# 创建子图并绘图
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
line1, = ax1.plot(x, y1, color='tab:blue')
line2, = ax2.plot(x, y2, color='tab:orange')

# 固定Y轴范围
ax1.set_ylim(y1_range)
ax2.set_ylim(y2_range)

# 可选:把轴边框向外偏移,进一步避免遮挡
ax1.spines['left'].set_position(('outward', 10))
ax2.spines['right'].set_position(('outward', 10))

# 添加图例
fig.legend([line1, line2], ['主Y轴数据', '次Y轴数据'], loc='upper right')
plt.show()

2. 批量图表的统一范围设置

如果要批量生成对比图表,先遍历所有数据集计算全局的主/次轴最大绝对值,再统一生成固定范围,确保所有图表轴范围一致:

# 模拟多组数据集
datasets = [
    (np.sin(x), 0.5*np.cos(x)*10),
    (2*np.sin(x+1), 0.3*np.cos(x+1)*15),
    # 更多数据集...
]

# 全局计算主/次轴的最大绝对值
global_max_abs_y1 = 0
global_max_abs_y2 = 0
for y1_data, y2_data in datasets:
    current_max_y1 = max(abs(y1_data.min()), abs(y1_data.max()))
    current_max_y2 = max(abs(y2_data.min()), abs(y2_data.max()))
    global_max_abs_y1 = max(global_max_abs_y1, current_max_y1)
    global_max_abs_y2 = max(global_max_abs_y2, current_max_y2)

# 生成全局固定范围
margin = 0.1 * global_max_abs_y1
global_y1_range = [-global_max_abs_y1 - margin, global_max_abs_y1 + margin]
scale = global_max_abs_y2 / global_max_abs_y1
global_y2_range = [-scale*(global_max_abs_y1 + margin), scale*(global_max_abs_y1 + margin)]

# 批量生成图表
for i, (y1_data, y2_data) in enumerate(datasets):
    fig, ax1 = plt.subplots()
    ax2 = ax1.twinx()
    ax1.plot(x, y1_data, color='tab:blue')
    ax2.plot(x, y2_data, color='tab:orange')
    ax1.set_ylim(global_y1_range)
    ax2.set_ylim(global_y2_range)
    plt.title(f'图表 {i+1}')
    plt.savefig(f'chart_{i+1}.png')
    plt.close()

3. 替代方案:让数据显示在边框前方

如果不想调整轴范围,直接调整绘图层级,让数据压在边框上方:

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

# 给数据设置更高的z-order,让它显示在边框前面
ax1.plot(x, y1, color='tab:blue', zorder=3)
ax2.plot(x, y2, color='tab:orange', zorder=3)

# 把轴边框的z-order设为低于数据
ax1.spines['left'].set_zorder(2)
ax1.spines['bottom'].set_zorder(2)
ax2.spines['right'].set_zorder(2)

# 固定原有的Y轴范围
ax1.set_ylim([y1_min, y1_max])
ax2.set_ylim([y2_min, y2_max])

plt.show()

内容的提问来源于stack exchange,提问作者jdkworld

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 08:33:32