如何在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
相关产品推荐
相关产品推荐

