循环绘制DataFrame指定列为子图异常:多图独立显示而非合并
问题分析与解决方案
你的代码问题出在子图创建时机错误,以及初始的ax字典未绑定到统一画布:
- 先初始化空字典
ax = {},循环中调用ax[pos-1].plot时,因字典无对应键,Matplotlib会自动创建独立新画布,导致弹出6个单独窗口。 - 最后执行
fig, ax = plt.subplots(6, 1, sharex=True),这个语句创建的是全新空白画布,所以出现第7个空窗口。
修正后的完整代码
import numpy as np import pandas as pd import matplotlib.pyplot as plt # 先创建统一画布的6个子图,sharex=True共享x轴 fig, ax = plt.subplots(6, 1, sharex=True, figsize=(10, 12)) # 初始化统计量字典 SD = {} LCL = {} UCL = {} for (name, values) in df.items(): if '.VALUE' in name: # 获取位置编号 pos = int(name[9]) # 计算移动极差列 df['MR_'+str(pos)] = np.abs(df[name].shift(1) - df[name]) # 计算均值 Mean = df[name].mean() Mean_MR = df['MR_'+str(pos)].mean() # 计算标准差、上下控制限 SD[pos-1] = Mean_MR / 1.128 LCL[pos-1] = Mean - SD[pos-1] * 3 UCL[pos-1] = Mean + SD[pos-1] * 3 # 标记异常值 df['Outlier_' + str(pos)] = np.where( (df[name] > UCL[pos-1]) | (df[name] < LCL[pos-1]), df[name], np.nan ) # 在对应子图上绘图:先画正常数据点,再画异常点 ax_idx = pos - 1 # 绘制POSITION_x.VALUE(蓝色点) ax[ax_idx].plot(df.index, df[name], 'o', color='blue', markersize=2, label=f'POSITION_{pos}.VALUE') # 绘制OUTLIER_x(红色点) ax[ax_idx].plot(df.index, df['Outlier_' + str(pos)], 'o', color='red', markersize=3, label=f'Outlier_{pos}') # 添加控制限水平线 ax[ax_idx].axhline(LCL[ax_idx], color='green', linestyle='--', label='LCL') ax[ax_idx].axhline(UCL[ax_idx], color='green', linestyle='--', label='UCL') # 添加子图标题和图例 ax[ax_idx].set_title(f'POSITION_{pos} Data & Outliers') ax[ax_idx].legend(fontsize=8) # 只在最后一个子图添加x轴标签 ax[-1].set_xlabel('Index') # 自动调整布局,避免重叠 plt.tight_layout() plt.show()
关键修改点
- 提前创建子图:把
fig, ax = plt.subplots(...)移到循环最前面,确保所有绘图绑定到同一画布的子图上。 - 移除无效的
ax = {}:现在ax是Matplotlib返回的Axes对象数组,直接通过索引ax[pos-1]调用对应子图。 - 修正绘图参数:
plot方法不支持use_index=True,直接传入df.index作为x轴;分开绘制正常点和异常点,逻辑更清晰。 - 增强可视化:添加控制限水平线、子图标题、图例,以及自动布局优化。
内容的提问来源于stack exchange,提问作者ChemEnger
相关产品推荐
相关产品推荐

