3. Python环境与核心库准备

质量控制图不是“画一张图”那么简单,它是一条从数据读取、统计量计算、控制限判定到图形输出的完整链路。在 Python 中实现这条链路,第一步不是写绘图代码,而是把环境与依赖固定下来,让后续每一张控制图都能在相同条件下复现。本节围绕三件事展开:核心库各自承担什么职责、如何安装并锁定版本、如何搭出一个可复用的绘图脚本骨架。

一、五个核心库的分工

食品质量控制场景中,典型数据是“批次—检测时间—指标值”的二维表,例如某乳品厂每小时抽取一瓶成品测定蛋白质含量,连续采集 25 个子组。围绕这类数据,五个库形成清晰的分层:

  • pandas:负责数据载入与整形。读取 CSV/Excel 检测记录、按子组编号分组、计算子组均值与极差,都是它的工作。
  • numpy:负责数值计算底座。控制限系数、标准差、滑动极差等向量化运算由它完成,pandas 的许多底层运算也依赖它。
  • scipy:负责统计分布与检验。计算正态分布分位数、过程能力指数相关的分布概率、异常点检验时使用。
  • matplotlib:负责静态图形输出。控制图需要精确控制中心线、上下控制限、子组点与连线,matplotlib 的底层控制能力最合适,也便于导出 PNG/PDF 归档。
  • plotly:负责交互图形。用于内部评审或网页展示,鼠标悬停可查看批次号与检测值,便于快速定位越限点。

需要强调的是,matplotlib 与 plotly 不是二选一,而是“归档用静态图、排查用交互图”的分工。脚本骨架应把两者都预留接口,但默认输出静态图,避免每次运行都生成网页文件。

二、安装与版本管理

推荐使用虚拟环境隔离项目依赖,避免不同课程或项目之间版本冲突。以 conda 为例:

conda create -n spc python=3.11 -y
conda activate spc
pip install pandas==2.2.2 numpy==1.26.4 scipy==1.13.1 \
            matplotlib==3.9.0 plotly==5.22.0
pip freeze > requirements.txt

版本锁定是本节的关键动作。控制图对数值精度敏感,numpy 大版本升级可能改变默认数据类型或运算行为,matplotlib 的样式默认值也会随版本调整。把 requirements.txt 纳入版本库,其他同事执行 pip install -r requirements.txt 即可得到一致环境。若使用纯 pip 方案,可用 python -m venv spc_env 创建环境,步骤等价。

安装完成后应做一次最小验证,确认五个库均可导入且版本符合预期:

import pandas, numpy, scipy, matplotlib, plotly
print(pandas.__version__, numpy.__version__, scipy.__version__)
print(matplotlib.__version__, plotly.__version__)
matplotlib.use("Agg")   # 无界面环境导出图片

其中 matplotlib.use("Agg") 常被忽略。在服务器或定时任务中运行脚本时,没有图形界面,若不指定后端,保存图片可能报错或卡住。把它写进骨架的入口处,是一次性解决隐患的做法。

三、可复用脚本骨架的结构

一个可复用的控制图脚本,应当把“数据”“参数”“计算”“绘图”四类内容分开,而不是把所有代码堆在一个文件里顺序执行。下面给出一个精简但完整的骨架,目录与职责如下:

spc_tool/
├── config.py        # 列名映射、子组大小、控制图类型等参数
├── loader.py        # 读取检测数据并做基础校验
├── stats.py         # 子组统计量、控制限、判异规则
├── plot_static.py   # matplotlib 静态控制图
├── plot_interactive.py  # plotly 交互控制图
└── run.py           # 入口:串联上述模块

核心思路是:stats.py 只返回数值结果(中心线、UCL、LCL、越限点索引),不碰任何绘图对象;两个绘图模块只接收这些数值并负责呈现。这样更换图形库时,统计逻辑完全不用改。下面是一个可直接运行的最小示例,以 X-R 控制图为例,子组大小为 5:

# stats.py 片段
import numpy as np

A2, D3, D4 = 0.577, 0.0, 2.114   # n=5 的常用系数

def xbar_r_chart(df, value_col, group_col):
    g = df.groupby(group_col)[value_col]
    xbar = g.mean().to_numpy()
    r = (g.max() - g.min()).to_numpy()
    xbarbar, rbar = xbar.mean(), r.mean()
    return {
        "xbar": xbar, "r": r,
        "x_cl": xbarbar,
        "x_ucl": xbarbar + A2 * rbar,
        "x_lcl": xbarbar - A2 * rbar,
        "r_cl": rbar,
        "r_ucl": D4 * rbar,
        "r_lcl": D3 * rbar,
    }

绘图模块拿到字典后,只需按 x_ucl、x_cl、x_lcl 画三条水平线,再把 xbar 序列连成折线,并对超出控制限的点用红色标记。判异规则(如连续 7 点位于中心线同侧)也放在 stats.py 中,返回布尔掩码,绘图模块据此着色。这种“计算与呈现分离”的结构,是后续章节反复复用同一骨架的基础。

控制图脚本骨架:数据流与模块职责 检测数据 CSV / Excel 批次·时间·指标值 loader.py pandas 读取 列名校验·缺失处理 stats.py numpy / scipy 计算 CL·UCL·LCL·判异掩码 plot_static.py matplotlib 归档图 plot_interactive.py plotly 交互排查 config.py 列名映射 · 子组大小 n=5 · 控制图类型 · 输出路径 示例输出:X-R 控制图(n=5,25 个子组,数据为教学假设值) UCL CL LCL 越限点 越限点

四、骨架落地时的几个实用约定

第一,列名映射放在 config.py 中,而不是散落在代码里。食品企业检测表的列名常为“样品编号”“检验时间”“蛋白质(g/100g)”这类中文,脚本内部统一映射为 group、time、value,换一家工厂只需改配置,不动逻辑。

第二,子组划分必须显式声明。控制图的统计量依赖子组大小 n,n 不同则 A2、D3、D4 系数不同。骨架中把 n 作为参数传入,并在 stats.py 内用字典查表,避免硬编码错误。

第三,输出路径与文件名带时间戳。例如 output/xbar_r_20250612_0930.png,便于追溯每次运行对应的数据版本。配合 requirements.txt 与数据文件哈希,就构成了最基本的可复现记录。

建议在骨架中加一个 run.py --check 模式:只读取数据、打印子组数、缺失值数量与计算出的控制限,不生成图片。数据接入新工厂时先用它核对,比直接出图更容易发现列名错位或子组划分错误。