Matplotlib绘制双Y轴图表完全指南


双Y轴图表是数据可视化中常见的图表类型,它允许我们在同一图表中展示两个不同量纲的数据系列。本文将全面介绍使用Matplotlib创建双Y轴图表的方法,包括基础实现、样式定制以及常见应用场景。

一、双Y轴图表基础

1. 双Y轴图表适用场景

双Y轴图表特别适合以下情况:

  • 比较两个不同单位但有关联的数据(如温度和降雨量)
  • 展示同一指标的不同量级变化(如销售额和增长率)
  • 对比不同量纲的趋势关系(如价格和交易量)

2. 基本实现方法

Matplotlib通过twinx()twiny()方法实现双轴,最常用的是twinx()(共享X轴的双Y轴):

import matplotlib.pyplot as plt
import numpy as np

# 创建图形和第一个轴
fig, ax1 = plt.subplots(figsize=(10, 6))

# 生成示例数据
x = np.arange(0, 10, 0.1)
y1 = np.sin(x)
y2 = np.exp(x) * 1000

# 绘制第一个Y轴数据
ax1.plot(x, y1, 'b-', label='sin(x)')
ax1.set_xlabel('X轴')
ax1.set_ylabel('sin(x)', color='b')
ax1.tick_params(axis='y', labelcolor='b')

# 创建第二个Y轴
ax2 = ax1.twinx()
ax2.plot(x, y2, 'r-', label='exp(x)')
ax2.set_ylabel('exp(x)', color='r')
ax2.tick_params(axis='y', labelcolor='r')

# 添加标题和图例
plt.title('双Y轴示例图表')
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax2.legend(lines1 + lines2, labels1 + labels2, loc='upper left')

plt.show()

二、样式定制技巧

1. 轴标签对齐

# 调整第二个Y轴标签位置避免重叠
ax2.yaxis.set_label_coords(1.1, 0.5)  # (x,y)坐标

2. 网格线控制

# 只显示主Y轴的网格线
ax1.grid(True)
ax2.grid(False)

3. 颜色与线型搭配

# 使用不同线型和颜色增强区分度
ax1.plot(x, y1, 'b--', linewidth=2, label='线性增长')
ax2.plot(x, y2, 'r:', linewidth=2, label='指数增长')

4. 轴范围同步

# 同步X轴范围
ax1.set_xlim(0, 10)
ax2.set_xlim(0, 10)

# 设置不同的Y轴范围
ax1.set_ylim(-1.5, 1.5)
ax2.set_ylim(0, 22000)

三、高级应用场景

1. 柱状图+折线图组合

# 数据准备
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May']
sales = [120, 145, 160, 185, 210]
growth_rate = [0.15, 0.12, 0.18, 0.22, 0.20]

fig, ax1 = plt.subplots(figsize=(10, 6))

# 柱状图(主Y轴)
bars = ax1.bar(months, sales, color='skyblue', alpha=0.7, label='销售额(万元)')
ax1.set_ylabel('销售额(万元)')

# 折线图(次Y轴)
ax2 = ax1.twinx()
line = ax2.plot(months, growth_rate, 'r-o', linewidth=2, label='增长率')
ax2.set_ylabel('增长率', color='r')
ax2.tick_params(axis='y', labelcolor='r')
ax2.set_ylim(0, 0.3)  # 固定增长率范围

# 添加数据标签
for bar in bars:
    height = bar.get_height()
    ax1.text(bar.get_x() + bar.get_width()/2., height,
             f'{height:.0f}',
             ha='center', va='bottom')

for x, y in zip(months, growth_rate):
    ax2.text(x, y, f'{y:.0%}',
             ha='center', va='bottom', color='r')

# 合并图例
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')

plt.title('销售额与增长率对比')
plt.show()

2. 三个Y轴实现

from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA

# 创建主轴
host = host_subplot(111, axes_class=AA.Axes)
plt.subplots_adjust(right=0.75)

# 创建两个附加轴
par1 = host.twinx()
par2 = host.twinx()

# 调整第二个附加轴位置
offset = 60
new_fixed_axis = par2.get_grid_helper().new_fixed_axis
par2.axis["right"] = new_fixed_axis(loc="right",
                                    axes=par2,
                                    offset=(offset, 0))
par2.axis["right"].toggle(all=True)

# 绘制数据
host.set_xlim(0, 10)
host.set_ylim(0, 10)

host.set_xlabel("X轴")
host.set_ylabel("Y轴1")
par1.set_ylabel("Y轴2")
par2.set_ylabel("Y轴3")

p1, = host.plot([0, 1, 2], [0, 1, 2], label="Y1")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Y2")
p3, = par2.plot([0, 1, 2], [0, 2, 4], label="Y3")

host.legend()

host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())
par2.axis["right"].label.set_color(p3.get_color())

plt.show()

3. 共享双Y轴子图

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))

# 第一个子图
ax1.plot(x, y1, 'b-')
ax1.set_ylabel('Y1', color='b')
ax1_2 = ax1.twinx()
ax1_2.plot(x, y2, 'r-')
ax1_2.set_ylabel('Y2', color='r')

# 第二个子图
ax2.plot(x, np.cos(x), 'g-')
ax2.set_ylabel('Y3', color='g')
ax2_2 = ax2.twinx()
ax2_2.plot(x, np.log(x+1), 'm-')
ax2_2.set_ylabel('Y4', color='m')

plt.tight_layout()
plt.show()

四、常见问题解决方案

1. 图例合并问题

# 更优雅的图例合并方法
from matplotlib.lines import Line2D

# 创建自定义图例元素
legend_elements = [
    Line2D([0], [0], color='b', lw=2, label='系列1'),
    Line2D([0], [0], color='r', lw=2, label='系列2'),
    Line2D([0], [0], marker='o', color='w', label='数据点',
           markerfacecolor='g', markersize=10)
]

ax1.legend(handles=legend_elements, loc='upper right')

2. 轴刻度对齐

# 使两个Y轴的0刻度对齐
def align_yaxis(ax1, ax2):
    y1_min, y1_max = ax1.get_ylim()
    y2_min, y2_max = ax2.get_ylim()

    ax1.set_ylim(y1_min, y1_max)
    ax2.set_ylim(y1_min * (y2_max/y1_max), y1_max * (y2_max/y1_max))

align_yaxis(ax1, ax2)

3. 轴标签重叠

# 调整边距和标签位置
plt.subplots_adjust(right=0.85)  # 为第二个Y轴留出空间
ax2.yaxis.set_label_coords(1.15, 0.5)  # 调整第二个Y轴标签位置

五、样式美化进阶

1. 使用Seaborn风格

import seaborn as sns

sns.set_style("whitegrid")
fig, ax1 = plt.subplots(figsize=(10, 6))

# 绘图代码...

2. 自定义轴线样式

# 设置轴线样式
ax1.spines['left'].set_color('blue')
ax1.spines['left'].set_linewidth(2)
ax2.spines['right'].set_color('red')
ax2.spines['right'].set_linewidth(2)

# 隐藏顶部和右侧轴线
ax1.spines['top'].set_visible(False)
ax2.spines['top'].set_visible(False)

3. 添加填充效果

# 在双Y轴图表中添加填充
ax1.fill_between(x, 0, y1, color='blue', alpha=0.1)
ax2.fill_between(x, 0, y2, color='red', alpha=0.1)

六、交互式双Y轴图表

1. 使用Plotly创建交互式双Y轴图表

import plotly.graph_objects as go
from plotly.subplots import make_subplots

# 创建带双Y轴的子图
fig = make_subplots(specs=[[{"secondary_y": True}]])

# 添加轨迹
fig.add_trace(
    go.Scatter(x=x, y=y1, name="sin(x)"),
    secondary_y=False,
)

fig.add_trace(
    go.Scatter(x=x, y=y2, name="exp(x)"),
    secondary_y=True,
)

# 设置轴标签
fig.update_yaxes(title_text="<b>sin(x)</b>", secondary_y=False)
fig.update_yaxes(title_text="<b>exp(x)</b>", secondary_y=True)

fig.show()

2. 使用mpld3实现Matplotlib交互

import mpld3

fig, ax1 = plt.subplots(figsize=(10, 6))
ax1.plot(x, y1, 'b-', label='sin(x)')
ax1.set_ylabel('sin(x)')

ax2 = ax1.twinx()
ax2.plot(x, y2, 'r-', label='exp(x)')
ax2.set_ylabel('exp(x)')

mpld3.display(fig)  # 在浏览器中显示交互式图表

七、最佳实践与注意事项

  1. 避免误导:确保双Y轴的使用不会造成数据关系的误读
  2. 清晰标注:明确标注每个Y轴代表的含义和单位
  3. 颜色区分:使用对比色明确区分不同轴的数据系列
  4. 比例协调:当比较趋势时,考虑调整比例使两条曲线有可比性
  5. 慎用三轴:除非绝对必要,否则避免使用三个Y轴,这会大大增加图表复杂度

通过本指南,你应该已经掌握了使用Matplotlib创建各种双Y轴图表的全面技能。从基础实现到高级定制,从静态图表到交互式可视化,这些技术将帮助你更有效地展示多维数据关系。记住,好的数据可视化不仅要技术正确,更要传达清晰准确的信息。

,

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注