如何在含极端异常值的箱线图中截断坐标轴?
带极端异常值的箱线图X轴截断实现方案
问题背景
用matplotlib+seaborn绘制箱线图时,因存在2000天的极端异常值,X轴范围被拉得过大,导致箱型占比极小。需要在700-1750区间截断X轴(用双斜杠标记),既保留异常值,又放大展示正常数据的箱型。此前尝试将列重命名为days_after并生成days_before列,拆分X轴绘图失败,现提供可行解决方法。
示例数据集
import pandas as pd data = { 'T1': ['ja', 'ja', 'ja', 'ja', 'ja', 'nein', 'nein', 'nein', 'nein', 'nein', 'ja', 'ja', 'ja', 'ja', 'ja', 'nein', 'nein', 'nein', 'nein', 'nein', 'ja', 'ja', 'ja', 'ja', 'ja', 'nein', 'nein', 'nein', 'nein'], 'days': [2000,200,200,400,400,100,500,50,400,600,30,200,200,400,400, 100,100,50,400,600,30,200,200,400,400,100,100,50,400] } df = pd.DataFrame(data)
初始绘图代码(问题复现)
import seaborn as sns import numpy as np import matplotlib.pyplot as plt %matplotlib inline f, ax = plt.subplots(figsize=(15,5)) sns.boxplot(data=df, x='days', y='T1', hue='T1', ax=ax) ax.set_xlabel('days') ax.legend().remove() plt.show()
此前尝试的问题分析
之前的代码存在两处关键错误:
- 未定义
days_after列就直接使用,导致报错 - 用负数反转数据的逻辑不合理,拆分后的X轴范围设置与需求不匹配,无法实现正常数据区与异常值区的分离展示
可行解决方案
方案一:使用matplotlib原生断轴功能(推荐)
通过创建共享Y轴的两个子图,分别展示正常数据区和异常值区,再添加截断标记模拟断轴效果。
import seaborn as sns import numpy as np import matplotlib.pyplot as plt %matplotlib inline # 创建两个共享Y轴的子图,调整宽度比例(正常数据区占比更大) fig, (ax1, ax2) = plt.subplots(ncols=2, sharey=True, figsize=(10,5), gridspec_kw={'width_ratios': [4, 1]}) # 左轴绘制正常数据(隐藏异常值) sns.boxplot(data=df, x='days', y='T1', hue='T1', ax=ax1, showfliers=False) # 右轴绘制全量数据(仅显示异常值及附近范围) sns.boxplot(data=df, x='days', y='T1', hue='T1', ax=ax2) # 移除冗余图例 ax1.legend().remove() ax2.legend().remove() # 设置X轴范围:左轴显示0-700,右轴显示1750-2100 ax1.set_xlim(0, 700) ax2.set_xlim(1750, 2100) # 隐藏中间边框,模拟截断效果 ax1.spines['right'].set_visible(False) ax2.spines['left'].set_visible(False) ax2.yaxis.set_ticks_position('none') # 隐藏右轴Y刻度 # 添加截断标记(双斜杠) d = .015 # 控制斜线倾斜程度 kwargs = dict(transform=ax1.transAxes, color='k', clip_on=False) ax1.plot((1-d, 1+d), (-d, +d), **kwargs) ax1.plot((1-d, 1+d), (1-d, 1+d), **kwargs) kwargs.update(transform=ax2.transAxes) ax2.plot((-d, +d), (1-d, 1+d), **kwargs) ax2.plot((-d, +d), (-d, +d), **kwargs) # 设置轴标签并调整子图间距 ax1.set_xlabel('days') ax2.set_xlabel('') plt.subplots_adjust(wspace=0.05) plt.show()
方案二:数据拆分简化版
将异常值单独提取,用两个子图分别展示正常箱型和异常值点:
import seaborn as sns import numpy as np import matplotlib.pyplot as plt %matplotlib inline fig, (ax1, ax2) = plt.subplots(ncols=2, sharey=True, figsize=(10,5), gridspec_kw={'width_ratios': [4, 1]}) # 左轴:正常数据箱线图 sns.boxplot(data=df[df['days']<=700], x='days', y='T1', hue='T1', ax=ax1) # 右轴:仅绘制异常值(散点图展示) sns.stripplot(data=df[df['days']>1750], x='days', y='T1', hue='T1', ax=ax2, jitter=False, size=8) # 移除图例并设置轴范围 ax1.legend().remove() ax2.legend().remove() ax1.set_xlim(0,700) ax2.set_xlim(1750,2100) # 隐藏中间边框并添加截断标记 ax1.spines['right'].set_visible(False) ax2.spines['left'].set_visible(False) ax2.yaxis.set_ticks_position('none') d = .015 kwargs = dict(transform=ax1.transAxes, color='k', clip_on=False) ax1.plot((1-d,1+d), (-d,+d), **kwargs) ax1.plot((1-d,1+d), (1-d,1+d), **kwargs) kwargs.update(transform=ax2.transAxes) ax2.plot((-d,+d), (1-d,1+d), **kwargs) ax2.plot((-d,+d), (-d,+d), **kwargs) plt.subplots_adjust(wspace=0.05) plt.show()
内容的提问来源于stack exchange,提问作者Kajette
相关产品推荐
相关产品推荐

