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

如何在Matplotlib子图中为家域图的双Y轴设置断轴?

问题

我有两个DataFrame,需要绘制包含两个子图的图表:

  • 第一个子图展示每日气温与月均气温;
  • 第二个子图展示群体平均家域宽度与个体家域宽度的月度数据。

当前图表绘制正常,但家域图的双Y轴中,活动范围较小个体的季节性变化被过度压缩,因此需为该双Y轴设置断轴。现有代码如下:

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10), sharex=False,
                              gridspec_kw={'height_ratios': [1, 3]})

# Plot 1: Daily temperature with mean monthly temperatures
sns.lineplot(data=temperature_df, x='date', y='temperature', ax=ax1, label='Daily Temperature')

# Convert year-month to datetime for plotting mean monthly temperatures
temperature_mean_df['date'] = pd.to_datetime(temperature_mean_df['year1'].astype(str) + '-' + temperature_mean_df['month1'].astype(str), format='%Y-%B')
temperature_mean_df['month2'] = temperature_mean_df['date'].dt.strftime('%Y-%B')


sns.lineplot(data=temperature_mean_df, x='date', y='temperature', ax=ax1, label='Mean Monthly Temperature', color='red', marker='o')
ax1.axhline(0, color='grey', linestyle='--')
ax1.set_title('Daily temperature 2019/2020')
ax1.set_xlabel('')
ax1.set_ylabel('Temperature (°C)')
ax1.set_ylim(-2, 17)  # Set the y-axis limits from -3 to +13
ax1.legend()

# Plot 2: Home range amplitude
sns.lineplot(data=home_range_df, x='month1', y='HUP', hue='Tag', ax=ax2, legend=True, palette=palette19)
# Create a secondary y-axis
ax2_right = ax2.twinx()
# Plot the mean home range amplitude with error bars on the secondary y-axis
ax2_right.errorbar(home_range_mean_df['month1'], home_range_mean_df['mean'], yerr=home_range_mean_df['sem'], fmt='o', color='gray', alpha=0.5, capsize=5)
# Plot the mean home range amplitude on the secondary y-axis
sns.barplot(data=home_range_mean_df, x='month1', y='mean', ax=ax2_right, alpha=0.5, color='blue')
ax2.set_title('')
ax2.set_xlabel('Date')
ax2.set_ylabel('Habitat Usage Potential m2 (Individuals)')
ax2_right.set_ylabel('Mean Habitat Usage Potential m2')

ax1.xaxis.set_major_locator(MonthLocator())
# Set the date format on the x-axis for both plots
date_formatter = DateFormatter('%b%Y')
ax1.xaxis.set_major_formatter(date_formatter)
ax2.xaxis.set_major_formatter(date_formatter)
plt.setp(ax1.get_xticklabels(), rotation=45, ha='right')
plt.setp(ax2.get_xticklabels(), rotation=45, ha='right')

plt.tight_layout()
plt.show()

请问需要修改哪些代码来实现家域图双Y轴的断轴效果?

解决方案

要实现家域图的双Y轴断轴,核心是把左右两个Y轴各拆分为高低两个区间,用子轴分别承载不同范围的数据,再添加断轴标识。具体修改步骤如下:

1. 导入断轴所需工具

在代码开头添加ConnectionPatch的导入,用于绘制断轴的斜线分隔符:

from matplotlib.patches import ConnectionPatch

2. 拆分Y轴为高低区间

根据你的实际数据定义断轴的数值区间(示例数值需根据你的数据调整),然后为左右两侧各创建两个子轴,分别对应低区间和高区间。

3. 分区间绘制数据

将个体数据和均值数据按数值范围拆分,分别绘制到对应的子轴上,避免小范围数据被压缩。

4. 添加断轴标识

用ConnectionPatch在高低子轴之间绘制斜线,明确标识断轴。

修改后的完整代码

import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
from matplotlib.dates import MonthLocator, DateFormatter
from matplotlib.patches import ConnectionPatch

# 假设你的DataFrame已提前定义:temperature_df, temperature_mean_df, home_range_df, home_range_mean_df, palette19

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10), sharex=False,
                              gridspec_kw={'height_ratios': [1, 3]})

# ---------------------- 第一个子图:气温图(无需修改) ----------------------
sns.lineplot(data=temperature_df, x='date', y='temperature', ax=ax1, label='Daily Temperature')

# Convert year-month to datetime for plotting mean monthly temperatures
temperature_mean_df['date'] = pd.to_datetime(temperature_mean_df['year1'].astype(str) + '-' + temperature_mean_df['month1'].astype(str), format='%Y-%B')
temperature_mean_df['month2'] = temperature_mean_df['date'].dt.strftime('%Y-%B')

sns.lineplot(data=temperature_mean_df, x='date', y='temperature', ax=ax1, label='Mean Monthly Temperature', color='red', marker='o')
ax1.axhline(0, color='grey', linestyle='--')
ax1.set_title('Daily temperature 2019/2020')
ax1.set_xlabel('')
ax1.set_ylabel('Temperature (°C)')
ax1.set_ylim(-2, 17)
ax1.legend()

# ---------------------- 第二个子图:家域图(修改部分) ----------------------
# 1. 定义断轴区间(根据实际数据调整数值)
# 左侧个体轴区间
y_low_ind = 0
y_high_ind = 2000
y_jump_ind = 10000  # 断轴跳跃点
y_high_high_ind = 15000
# 右侧均值轴区间
y_low_mean = 0
y_high_mean = 2000
y_jump_mean = 10000
y_high_high_mean = 15000

# 2. 创建左侧高低子轴(个体数据)
ax2_low = ax2
ax2_high = ax2.twinx()
ax2_low.set_ylim(y_low_ind, y_high_ind)
ax2_high.set_ylim(y_jump_ind, y_high_high_ind)
# 隐藏高轴的刻度标签和右侧脊柱,避免重复
ax2_high.set_yticklabels([])
ax2_high.spines['right'].set_visible(False)

# 分区间绘制个体数据
low_ind_data = home_range_df[home_range_df['HUP'].between(y_low_ind, y_high_ind)]
high_ind_data = home_range_df[home_range_df['HUP'].between(y_jump_ind, y_high_high_ind)]
sns.lineplot(data=low_ind_data, x='month1', y='HUP', hue='Tag', ax=ax2_low, legend=True, palette=palette19)
sns.lineplot(data=high_ind_data, x='month1', y='HUP', hue='Tag', ax=ax2_high, legend=False, palette=palette19)

# 3. 创建右侧高低子轴(均值数据)
ax2_right_low = ax2_low.twinx()
ax2_right_high = ax2_high.twinx()
ax2_right_low.set_ylim(y_low_mean, y_high_mean)
ax2_right_high.set_ylim(y_jump_mean, y_high_high_mean)
# 隐藏低轴的右侧脊柱
ax2_right_low.spines['right'].set_visible(False)

# 分区间绘制均值数据
low_mean_data = home_range_mean_df[home_range_mean_df['mean'].between(y_low_mean, y_high_mean)]
high_mean_data = home_range_mean_df[home_range_mean_df['mean'].between(y_jump_mean, y_high_high_mean)]
sns.barplot(data=low_mean_data, x='month1', y='mean', ax=ax2_right_low, alpha=0.5, color='blue')
ax2_right_low.errorbar(low_mean_data['month1'], low_mean_data['mean'], yerr=low_mean_data['sem'], fmt='o', color='gray', alpha=0.5, capsize=5)
sns.barplot(data=high_mean_data, x='month1', y='mean', ax=ax2_right_high, alpha=0.5, color='blue')
ax2_right_high.errorbar(high_mean_data['month1'], high_mean_data['mean'], yerr=high_mean_data['sem'], fmt='o', color='gray', alpha=0.5, capsize=5)

# 4. 设置轴标签
ax2_low.set_xlabel('Date')
ax2_low.set_ylabel('Habitat Usage Potential m2 (Individuals)')
ax2_right_high.set_ylabel('Mean Habitat Usage Potential m2')

# 5. 添加断轴斜线标识
# 左侧轴断轴斜线
con1 = ConnectionPatch(xyA=(0, y_high_ind), xyB=(0, y_jump_ind),
                      coordsA='data', coordsB='data',
                      axesA=ax2_low, axesB=ax2_high,
                      color='gray', linestyle='--')
ax2_high.add_artist(con1)
con2 = ConnectionPatch(xyA=(1, y_high_ind), xyB=(1, y_jump_ind),
                      coordsA='data', coordsB='data',
                      axesA=ax2_low, axesB=ax2_high,
                      color='gray', linestyle='--')
ax2_high.add_artist(con2)

# 右侧轴断轴斜线
con3 = ConnectionPatch(xyA=(1, y_high_mean), xyB=(1, y_jump_mean),
                      coordsA='data', coordsB='data',
                      axesA=ax2_right_low, axesB=ax2_right_high,
                      color='gray', linestyle='--')
ax2_right_high.add_artist(con3)
con4 = ConnectionPatch(xyA=(0, y_high_mean), xyB=(0, y_jump_mean),
                      coordsA='data', coordsB='data',
                      axesA=ax2_right_low, axesB=ax2_right_high,
                      color='gray', linestyle='--')
ax2_right_high.add_artist(con4)

# ---------------------- 轴格式设置(部分调整) ----------------------
ax1.xaxis.set_major_locator(MonthLocator())
date_formatter = DateFormatter('%b%Y')
ax1.xaxis.set_major_formatter(date_formatter)
ax2.xaxis.set_major_formatter(date_formatter)
plt.setp(ax1.get_xticklabels(), rotation=45, ha='right')
plt.setp(ax2.get_xticklabels(), rotation=45, ha='right')

plt.tight_layout()
plt.show()

关键说明

  • 必须根据你的实际数据调整断轴的区间数值,确保低区间覆盖个体数据的主要波动范围,高区间容纳大数值的均值数据;
  • 如果个体数据全部集中在低区间,可省略高区间的折线绘制;
  • 断轴斜线的位置可通过xyA和xyB的坐标微调,默认在轴的左右两端绘制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 02:50:01