数据读取与预处理
说真的,做批次质控的第一步往往不是跑统计模型,而是把数据老老实实读进来。我见过不少项目,花了80%的时间在数据清洗上,最后模型只跑了一分钟——但这一分钟的结果靠谱。所以这块儿我特别想跟你聊聊。
数据读取:从文件到DataFrame
数据来源基本就三种:CSV、Excel、SQL数据库。咱一个一个说。
CSV文件读取
CSV是最常见的格式。我刚开始用pandas读CSV时,最常踩的坑就是编码问题。
import pandas as pd
# 基础读取
df = pd.read_csv("batch_data.csv")
# 指定编码,拯救乱码
df = pd.read_csv("batch_data.csv", encoding="utf-8-sig")
# 大文件分块读取,避免内存爆炸
chunk_iter = pd.read_csv("huge_data.csv", chunksize=10000)
for chunk in chunk_iter:
process(chunk)
我的经验:遇到中文字段名乱码时,encoding="utf-8-sig" 比 utf-8 更稳。Windows 导出的 CSV 经常带 BOM 头。
还有一个参数你得留意——engine。C引擎快,但遇到复杂分隔符或大文件时容易报错。Python引擎虽然慢点,但容错性好很多。
# 遇到解析错误时,换Python引擎试试
df = pd.read_csv("messy_data.csv", engine="python", sep=r'\s+')
Excel文件读取
Excel比CSV麻烦一些,主要因为多sheet和格式问题。
# 读取第一个sheet
df = pd.read_excel("batch_records.xlsx")
# 指定sheet
df = pd.read_excel("batch_records.xlsx", sheet_name="Batch_2024")
# 读取所有sheet
all_sheets = pd.read_excel("batch_records.xlsx", sheet_name=None)
注意:Excel 文件里的合并单元格会引入 NaN。我曾经在分析一个生产批次表时,因为合并表头没处理,导致后续所有统计都跑偏了。建议先 fillna(method='ffill') 填充合并单元格。
SQL数据库读取
做大项目时,数据一般都在数据库里。我习惯用 sqlalchemy 加 pandas 的组合。
from sqlalchemy import create_engine
import pandas as pd
# 连接SQLite(本地测试用)
engine = create_engine("sqlite:///batch_control.db")
# 读取整张表
df = pd.read_sql("SELECT * FROM batch_records", engine)
# 带条件查询
df = pd.read_sql("""
SELECT batch_id, measure_value, time
FROM batch_records
WHERE measure_value IS NOT NULL
""", engine)
# 分页读取大表
chunk_iter = pd.read_sql("SELECT * FROM huge_table",
engine, chunksize=10000)
我个人习惯:生产数据库用 chunksize 分块读,既防止内存溢出,也避免把数据库连接拖死。你想想看,一个几千万行的表,直接全量读进来,内存和网络都受不了。
数据清洗:让脏数据变干净
数据读进来了,接下来就是“洗数据”。这一步决定了你后面所有质控分析的可信度。我自己踩过的坑,大多都跟数据清洗有关。
缺失值处理
缺失值用 NaN 表示。你怎么处理,取决于业务场景。
# 查看缺失情况
print(df.isnull().sum())
print(df.isnull().mean()) # 缺失比例
# 删除含缺失的行
df_clean = df.dropna()
# 按列删除(缺失比例超过50%的列)
df_clean = df.dropna(thresh=len(df) * 0.5, axis=1)
# 填充:均值/中位数/前向填充
df["temp"].fillna(df["temp"].mean(), inplace=True)
df["batch_id"].fillna(method="ffill", inplace=True)
避坑指南:我曾经在批处理质控中,直接删除了缺失超过10%的样本,结果把生产异常批次全删掉了——因为异常批次恰恰是缺失率最高的。结果模型跑出来一切正常,但实际生产早出问题了。所以删除前,一定先分析缺失原因。
| 处理方法 | 适用场景 | 风险提示 |
|---|---|---|
| 删除行 | 缺失比例小(<5%) | 可能丢失小样本异常信息 |
| 删除列 | 缺失比例大(>70%) | 可能丢掉关键批次特征 |
| 均值/中位数填充 | 连续型测量值 | 会降低数据方差 |
| 前向/后向填充 | 时间序列批次 | 需确保时间顺序正确 |
异常值处理
异常值判断不能只靠统计。我见过用3σ法则把正常波动当异常删掉的案例。说说常用的两种方法。
Z-Score方法(3σ法则)
from scipy import stats
import numpy as np
z_scores = np.abs(stats.zscore(df["measure_value"]))
# 保留Z-Score <= 3的数据
df_clean = df[z_scores <= 3]
IQR方法(四分位距法)
Q1 = df["measure_value"].quantile(0.25)
Q3 = df["measure_value"].quantile(0.75)
IQR = Q3 - Q1
# 定义上下限
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 过滤异常值
df_clean = df[(df["measure_value"] >= lower_bound) &
(df["measure_value"] <= upper_bound)]
我的建议:IQR方法对非正态分布更鲁棒。我在化工批次质控中遇到测量值偏态分布时,用Z-Score把所有高值都误判成异常了,换成IQR就好了。
数据预处理流程概览
说了这么多,我在项目里一般按这个流程来。说白了就是:先读进来,再看缺什么,最后处理异常。
核心理念:数据清洗不是一次完成的。我经常在质控分析后发现数据有问题,再回头调整清洗策略。这是一个迭代的过程,别指望一步到位。
实际项目中的小贴士
你可能以为数据清洗就是填缺失值、去异常值,其实远不止这些。我总结几个实际项目里常用的技巧:
- 先看数据概览:用
df.describe()、df.info()快速扫一眼,比直接上代码高效。我记得有一次8个字段的缺失率为0,但实际都是空字符串,describe直接暴露了。 - 保留原始数据副本:千万别在原数据上直接修改。我习惯加个
.copy(),出错了随时回滚。 - 日志记录清洗过程:删了多少行、填了什么值,记下来。这在后续质控溯源时非常关键。
# 保留数据清洗日志示例
log = {
"原始行数": len(df),
"缺失行数": int(df.isnull().any(axis=1).sum()),
"异常行数": outlier_mask.sum(),
"最终行数": len(df_clean)
}
print("清洗日志:", log)
嗯,到这里数据读取和预处理的基本套路你应该清楚了。多动手,多留意业务逻辑,这步功夫下足了,后面质控分析才能站得住脚。