为箱线图设置特定Y轴及解决重叠等绘制问题
箱线图绘制时间序列数据的问题解决
问题说明
尝试用箱线图绘制随时间(百万年单位)变化的数据时,遇到以下问题:
- 箱线图互相重叠,无法锁定Y轴范围
- 指定代表百万年的位置(如2.5)绘制时,箱线图过度聚集且尺寸过大,添加子图也无法解决
原始代码如下:
import pandas as pd import seaborn import seaborn as sns import matplotlib.pyplot as plt #default theme sns.set_theme() #Loading data set my_data = pd.read_csv("C:\Users\tchat\OneDrive\Documents\UQAM_MS\Samples\Ratio\CSV\NbTa_ages_test.csv") #see dataset print(my_data) #Check for non numerical data non_numeric_columns = my_data.select_dtypes(exclude=[float,int]).columns if len(non_numeric_columns) > 0: print(f"Non-numeric columns detected: {list(non_numeric_columns)}") my_data[non_numeric_columns]=my_data[non_numeric_columns].apply(pd.to_numeric, errors='coerce') #Check for missing values if my_data.isnull().values.any(): print("Missing values detected, filling with column means.") my_data = my_data.fillna(my_data.mean()) #Number of columns in the dataset num_columns = len(my_data.columns) print(f"Number of columns: {num_columns}") positions = [2.420, 2.458, 2.460, 2.461, 2.471, 2.472, 2.475, 2.476, 2.477, 2.478,2.480, 2.481, 2.482, 2.485, 2.5] if len(positions) != num_columns: raise ValueError(f"The length of positions ({len(positions)}) does not match the number of columns in the data ({num_columns}).") ax = my_data.boxplot(positions=positions, vert=False) ax.set_ylim(2.4,2.5) plt.grid(True, axis='y', which='major', linestyle='-', linewidth=0.5) plt.grid(True, axis='y', which='minor', linestyle=':', linewidth=0.5) plt.show()
输出的箱线图表现为多个箱体严重重叠,在2.4-2.5的Y轴区间内聚集在一起,尺寸过大导致无法区分单个箱体。
解决方案
1. 缩小箱线图宽度,避免重叠
使用boxplot的widths参数设置更小的箱体宽度,适配密集的时间位置。
2. 手动控制Y轴刻度与范围
由于时间位置的数值范围极小(仅0.1的跨度),matplotlib自动刻度逻辑会导致显示异常,需手动设置主/次刻度,确保Y轴锁定在目标范围。
3. 转换数据格式适配Seaborn(更推荐)
Pandas原生boxplot在处理密集数值位置时灵活性不足,改用Seaborn的boxplot需要先将宽格式数据转为长格式(通过pd.melt),这样更便于按时间轴映射箱体位置。
修改后的代码
import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from matplotlib.ticker import MultipleLocator # 设置主题 sns.set_theme() # 加载数据(路径加r避免转义问题) my_data = pd.read_csv(r"C:\Users\tchat\OneDrive\Documents\UQAM_MS\Samples\Ratio\CSV\NbTa_ages_test.csv") # 处理非数值与缺失值(保留原有逻辑) non_numeric_columns = my_data.select_dtypes(exclude=[float, int]).columns if len(non_numeric_columns) > 0: print(f"检测到非数值列: {list(non_numeric_columns)}") my_data[non_numeric_columns] = my_data[non_numeric_columns].apply(pd.to_numeric, errors='coerce') if my_data.isnull().values.any(): print("检测到缺失值,使用列均值填充。") my_data = my_data.fillna(my_data.mean()) # 为列名匹配时间位置,转换为长格式数据 positions = [2.420, 2.458, 2.460, 2.461, 2.471, 2.472, 2.475, 2.476, 2.477, 2.478, 2.480, 2.481, 2.482, 2.485, 2.5] my_data.columns = positions melted_data = my_data.melt(var_name="时间(Ma)", value_name="数值") # 创建绘图对象并设置尺寸 fig, ax = plt.subplots(figsize=(10, 6)) # 绘制箱线图,设置宽度避免重叠 sns.boxplot(data=melted_data, y="时间(Ma)", x="数值", vert=False, width=0.3, ax=ax) # 锁定Y轴范围,手动设置刻度 ax.set_ylim(2.4, 2.5) ax.yaxis.set_major_locator(MultipleLocator(0.01)) ax.yaxis.set_minor_locator(MultipleLocator(0.005)) # 添加网格 ax.grid(True, axis='y', which='major', linestyle='-', linewidth=0.5) ax.grid(True, axis='y', which='minor', linestyle=':', linewidth=0.5) # 优化布局 plt.tight_layout() plt.show()
关键修改说明
- 用
pd.melt将宽格式数据转为长格式,让Seaborn可以直接映射时间轴为Y轴 - 设置
width=0.3缩小箱体宽度,避免密集位置的重叠 - 手动设置Y轴主/次刻度,确保轴范围锁定且刻度清晰
- 使用
plt.tight_layout()优化布局,避免元素溢出
内容的提问来源于stack exchange,提问作者Tania Chatila
相关产品推荐
相关产品推荐

