矿物智能识别项目实战(一):从零开始清洗工业矿物数据

目录


项目背景与目标

拿到这个「矿物成分检测」项目时,打开原始数据文件,映入眼帘的是几千行由一线工人手工填写的 Excel 表格。表格里不仅有正常的数值,还有各种缺失值、错填的小数点、非法字符(如 \)、空格伪装成的"空值"......这些"脏数据"如果不处理,后续的特征工程和分类模型根本没法用。

我们第一步的目标很明确:把原始数据清洗成一份缺失值统一为 NaN、标签已编码的干净数据集,为后续的缺失值填充和模型训练打好基础。

本文就是这次清洗过程的完整记录,代码适用于绝大多数类似"人工填表"产生的工业数据,希望对同样踩坑的朋友有所帮助。


数据清洗:让混乱的原始数据变得可控

导入必要的库

首先导入 Pandas 做数据处理,Matplotlib 后续可视化备用,以及一个自定义的 fill_data 模块(后续填充缺失值时会用到)。

python 复制代码
import pandas as pd
import matplotlib.pyplot as plt
import fill_data  # 自定义填充模块

读取 Excel 数据

原始数据存储在 矿物数据.xlsx 中,直接使用 pd.read_excel() 读取。

python 复制代码
data = pd.read_excel("矿物数据.xlsx")

此时 data 是一个 DataFrame,包含"序号"、"矿物类型"以及其他矿物特征列(如元素含量、硬度等)。

删除特殊类别 'E'

数据集中只有一条记录的 矿物类型'E',属于异常或极少数类,对我们的分类任务没有意义,直接删除该行。

python 复制代码
data = data[data['矿物类型'] != 'E']  # 整行删除

检测缺失值(含空格、空字符串等)

工人填表时常会留空、敲空格,Pandas 的 .isnull() 可以识别 NaNNone,但不能自动把空格字符串当作缺失值 。不过,由于我们稍后会使用 pd.to_numeric(..., errors='coerce'),空格会在转换时变成 NaN,所以先直接统计当前缺失情况即可。

python 复制代码
null_num = data.isnull()                # 缺失处为 True
null_total = null_num.sum()             # 每列缺失值个数
print("各列缺失值统计:\n", null_total)

小提示 :如果某些列全是空格(肉眼看上去为空),现在 isnull() 可能统计不到。不要慌,后续 to_numeric 会把它们变为 NaN

分离特征 (X) 和标签 (y)

  • 标签列:矿物类型
  • 特征列:除了标签和无关的"序号"列
python 复制代码
X_whole = data.drop('矿物类型', axis=1).drop('序号', axis=1)  # 全部特征
y_whole = data['矿物类型']                                      # 全部标签

标签字符转数字

原始标签是 A、B、C、D 四个字母(E 已被删除)。大多数分类模型需要整数标签,所以建立一个映射字典并转换。

python 复制代码
label_dict = {"A": 0, "B": 1, "C": 2, "D": 3}
encoded_labels = [label_dict[label] for label in y_whole]
y_whole = pd.Series(encoded_labels, name='矿物类型')

转换后 y_whole 中的值变成 0,1,2,3。

异常文本转数值:一招清理所有"脏数据"

这是整个清洗流程中最关键的一步。原始特征列中可能存在:

  • 手误小数点:如 ..51.2.3
  • 非法字符:\/abc
  • 空格、空字符串
  • 科学记数法或其它不可解析的内容

pd.to_numeric(..., errors='coerce') 可以将能转换的转为浮点数,不能转换的全部设为 NaN。一行代码解决多种文本异常问题。

python 复制代码
for column_name in X_whole.columns:
    X_whole[column_name] = pd.to_numeric(X_whole[column_name], errors='coerce')
  • errors='coerce':转换失败时返回 NaN
  • 经过这一步,所有空格、乱字符、错填小数点都会变成 NaN

此时 X_whole 中的数值已经干净(缺失值统一为 NaN),可以进入下一阶段:缺失值可视化及填充。

检查清洗后的数据概览

python 复制代码
print("特征数据形状:", X_whole.shape)
print("标签数据形状:", y_whole.shape)
print("\n特征缺失值统计(转换后):\n", X_whole.isnull().sum())

输出示例:

复制代码
特征数据形状: (523, 12)
标签数据形状: (523,)

特征缺失值统计(转换后):
 SiO2        12
 Al2O3       34
 Fe2O3        7
 ...

这样我们就得到了一个缺失值统一为 NaN、标签已编码的干净数据集。


数据标准化、切分与缺失值填充

清洗后的特征数据 X_whole 中不同特征的量纲差异很大(例如某些元素含量上千,而某微量元素才零点几)。许多分类模型(如 SVM、逻辑回归、神经网络)对特征尺度敏感,因此需要先做 Z‑标准化,使每个特征均值为 0、标准差为 1。标准化统一了特征的数值范围,避免大数值特征主导模型训练。

Z‑标准化

python 复制代码
'''数据标准化:Z标准化'''
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_whole_Z = scaler.fit_transform(X_whole)
X_whole = pd.DataFrame(X_whole_Z, columns=X_whole.columns)  # Z标准化后转回DataFrame

数据集切分

为保证实验可复现,使用固定随机种子将数据划分为训练集(70%)和测试集(30%)。

python 复制代码
'''数据集切分'''
from sklearn.model_selection import train_test_split

x_train_w, x_test_w, y_train_w, y_test_w = \
    train_test_split(X_whole, y_whole, test_size=0.3, random_state=50000)

切分后训练集和测试集仍含有缺失值 NaN。接下来是项目的关键目标:对比多种缺失值填充策略对矿物分类精度的影响,从而找出最适合工业场景的填充方法。

工业矿物数据中的缺失往往不是完全随机的------工人可能因为样品未检测某项指标而留空,或者因书写错误导致数据无法解析。不同的填充方法会带来不同的信息引入偏差。

我们的实验思路是:在完全相同的训练/测试划分和标准化前提下,分别采用六种填充方案,后续将采用八种不同的分类模型(如随机森林、XGBoost、SVM、逻辑回归等),在六种填充方案下分别评估模型性能评估其测试集准确率、F1‑macro 等指标,最终确定矿物类型智能识别的最优数据预处理与模型组合方案。

六种缺失填充方案(调用 fill_data 模块)

项目内置了 fill_data 模块,其中实现了六种填充策略。

python 复制代码
'''6种缺失填充,此处调用均值方案'''
# # 1、只保留完整行数据(CCA)
# x_train_fill, y_train_fill = fill_data.cca_train_fill(x_train_w, y_train_w)
# x_test_fill, y_test_fill = fill_data.cca_test_fill(x_train_fill, y_train_fill, x_test_w, y_test_w)

# 2、使用平均值的方法对数据进行填充
x_train_fill, y_train_fill = fill_data.mean_train_fill(x_train_w, y_train_w)
x_test_fill, y_test_fill = fill_data.mean_test_fill(x_train_fill, y_train_fill, x_test_w, y_test_w)

# # 3、使用中位数的方法对数据进行填充
# x_train_fill, y_train_fill = fill_data.median_train_fill(x_train_w, y_train_w)
# x_test_fill, y_test_fill = fill_data.median_test_fill(x_train_fill, y_train_fill, x_test_w, y_test_w)

# # 4、使用众数的方法对数据进行填充
# x_train_fill, y_train_fill = fill_data.mode_train_fill(x_train_w, y_train_w)
# x_test_fill, y_test_fill = fill_data.mode_test_fill(x_train_w, y_train_w, x_test_w, y_test_w)

# # 5、线性回归算法对数据进行填充
# x_train_fill, y_train_fill = fill_data.lr_train_fill(x_train_w, y_train_w)
# x_test_fill, y_test_fill = fill_data.lr_test_fill(x_train_fill, y_train_w, x_test_w, y_test_w)

# # 6、随机森林算法对数据进行填充(训练集用线性回归填充,测试集用随机森林)
# x_train_fill, y_train_fill = fill_data.lr_train_fill(x_train_w, y_train_w)
# x_test_fill, y_test_fill = fill_data.rf_test_fill(x_train_fill, y_train_w, x_test_w, y_test_w)
各填充方案的思路与注意事项

CCA:直接删除含缺失值的行,适用于缺失 <5% 的场景,否则样本损失大且可能改变标签分布。

均值/中位数/众数填充:用训练集统计量填充缺失值。均值对异常值敏感,偏斜数据用中位数更稳;众数用于离散特征。

线性回归填充:将缺失特征作目标,用其他完整特征回归预测。需迭代(先填缺失少的列),假定特征间线性关系。

随机森林填充:能捕获非线性关系,对异常值鲁棒,但计算成本高。

填充后得到完整数值矩阵,后续用准确率、召回率等指标对比各方法优劣。

SMOTE 过采样:解决样本不均衡

经过缺失值填充后,训练集和测试集都已完整可用。但若原始数据中各类别样本数量悬殊,模型会偏向多数类,导致少数类识别率极低。此时需要过采样来平衡类别分布。

SMOTE(Synthetic Minority Over-sampling Technique)通过在少数类样本之间插值生成新样本,而非简单复制,能有效缓解过拟合风险。

python 复制代码
'''smote过采样,解决样本不均衡,仅扩充训练集'''
from imblearn.over_sampling import SMOTE

oversampler = SMOTE(k_neighbors=1, random_state=42)
os_x_train, os_y_train = oversampler.fit_resample(x_train_fill, y_train_fill)

这里设置 k_neighbors=1,即每个少数类样本只找最近邻的 1 个同类样本进行插值,适合样本量极少的情况;random_state=42 保证结果可复现。SMOTE 仅对训练集进行过采样,测试集保持原始分布,这样才能真实评估模型在真实场景下的表现。

保存处理后的数据

数据清洗、填充、过采样全部完成后,将训练集和测试集分别保存为 Excel 文件,供后续建模使用。

python 复制代码
'''处理后数据保存为Excel'''
data_train = pd.concat([os_y_train, os_x_train], axis=1).sample(frac=1, random_state=4)  # 打乱训练集
data_test = pd.concat([y_test_fill, x_test_fill], axis=1)  # 测试集不打乱

data_train.to_excel(r'./temp_data/训练数据集[平均值数据].xlsx', index=False)
data_test.to_excel(r'./temp_data/测试数据集[平均值数据].xlsx', index=False)

训练集用 .sample(frac=1) 打乱顺序,避免模型学到样本排列的虚假规律;测试集保持原始顺序,便于后续逐条分析预测结果。文件命名中标注了填充方案(平均值),方便对比不同填充策略的效果。

总结

写这篇流水线的时候,有几个坑和心得值得记下来:

1. 缺失值检测别只盯着 isnull()

df.isnull().sum() 只能抓到 NaN,但实际数据里还有空字符串 '''null' 字符串、不可见空格。某列看起来全是数字,isnull() 显示 0 缺失,结果模型跑出来一塌糊涂,后来发现是空格被当成了有效值。所以检测缺失时最好统一做一步:df.replace(r'^\s*$', np.nan, regex=True) 把空白字符串也转成 NaN。

2. 别总是想着写正则一个个匹配

几万条数据,错误类型千奇百怪:千分位逗号、中文单位、百分号、空格、特殊符号、字母混入......人眼不可能一个个去筛选预测哪里错了、错在哪里。写正则看似"可控",但每新增一种脏数据格式就要加一条规则,规则之间还可能冲突,维护成本指数级上升。更致命的是:你根本不知道数据里还有多少种你没见过的脏格式,正则只能处理你已知的,数据处理一直报错。

工业场景下的正确做法是两步走:

pd.to_numeric 能自动处理千分位逗号、正负号、科学计数法、前后空格等常见格式,一次调用覆盖 90% 的脏数据。剩下 10% 转成 NaN 后,你只需要看那几百条异常值,而不是几万条。这时候再针对这少量异常写正则或人工处理,效率高得多。

核心原则:先批量清洗,再聚焦异常。 不要试图用正则覆盖所有可能性,那是人肉运维的思路,不是数据工程的思路。

3. SMOTE 一定要在切分之后做

新手最容易犯的错误:先对整个数据集做 SMOTE 过采样,再切分训练集和测试集。这样测试集里会混入合成样本,评估结果虚高,上线就崩。正确顺序是:先切分,再只对训练集做 SMOTE。测试集保持原始分布,才能真实反映模型泛化能力。

4. 保存数据前打乱一下

sample(frac=1) 这步看着不起眼,但如果不打乱,训练时模型可能先学到全是 A 类、再学到全是 B 类,梯度更新震荡很大。打乱后每批数据分布更均匀,训练更稳定。

5. 填充方案别无脑选均值

均值填充快,但如果数据分布偏态严重(比如收入数据),均值会被极端值拉偏,不如中位数稳健。分类特征用众数。如果业务场景有规律(比如缺失值出现在某个时间段),用回归或随机森林填充效果更好。我一般先跑一遍均值/中位数/众数,对比一下填充后的分布直方图,再决定用哪个。

这些细节单独看都不难,但串起来就是"能跑"和"能上线"的区别。

相关推荐
雨水的早晨1 小时前
什么是SKill
人工智能·skill
gis分享者1 小时前
AI数字营销实测体验,产品推广创作体验
人工智能·csdn·产品推广·数字营销·体验
下班走回家1 小时前
RAG 技术的进化:从朴素检索到 Agentic RAG
开发语言·人工智能·python
Codebee1 小时前
做一款 AI-IDE 有多难 —— 从 OODER Studio 的现有实现谈起
人工智能
傅科摆 _ py1 小时前
AI Ping 平台使用教程
java·前端·人工智能
weixin_307779131 小时前
从“大海捞针”到“主动推理”:AI如何重塑云原生故障诊断的根因链
开发语言·人工智能·算法·自动化·原型模式
苏州邦恩精密1 小时前
江苏三维扫描仪定制:制造企业如何提升检测效率?
人工智能·科技·机器学习·自动化·制造
jinglong.zha1 小时前
AI视频全流程实战:广告/动画/短剧都适用,解决角色一致性+后期合成难题
人工智能·ai·音视频·光照贴图·叙事照片
2401_832298101 小时前
适配工业互联网场景,OpenClaw落地工厂智能运维,加速工业4.0无人化转型
大数据·人工智能