1. 项目背景与核心挑战
矿物分类是地质勘探和资源评估中的基础性工作。在实际项目中,我们常常会遇到实验室检测数据、野外勘探数据等多源异构数据集。这些原始数据往往存在检测误差、单位不统一、异常值干扰等问题,直接影响后续分类模型的准确性。
去年参与某矿区勘探项目时,我们就遇到了典型的数据质量问题:同一批样本的密度测量值中,部分数据出现数量级差异(如正常值2.7-3.3g/cm³中混入27g/cm³的记录);不同实验室提供的化学成分数据有的用百分比表示,有的用ppm单位;部分样本的关键特征字段存在大面积缺失。这些问题如果不经处理直接建模,会导致分类器性能下降30%以上。
2. 数据清洗全流程方法论
2.1 异常值检测与处理
异常值的识别是数据清洗的第一步。我们采用三级检测策略:
- 物理范围筛查:根据矿物学知识建立硬性边界。例如:
- 石英的密度范围2.65±0.05g/cm³
- 黄铁矿的硫含量必>40wt%
- 建立字段级校验规则表(见下表)
| 特征字段 | 有效范围 | 单位 | 依据 |
|---|---|---|---|
| Density | 1.0-20.0 | g/cm³ | 矿物物理手册 |
| SiO₂ | 0-100 | wt% | 化学组成限制 |
| Magnetic | 0-1 | - | 布尔类型 |
统计方法检测:
- 使用Tukey's fences方法:IQR=Q3-Q1,异常值边界为[Q1-1.5IQR, Q3+1.5IQR]
- 对偏态分布采用对数变换后再检测
- 示例代码:
def detect_outliers(df, col): q1 = df[col].quantile(0.25) q3 = df[col].quantile(0.75) iqr = q3 - q1 return df[(df[col] < q1-1.5*iqr) | (df[col] > q3+1.5*iqr)]
模型辅助检测:
- 使用Isolation Forest算法检测多维特征空间中的异常点
- 设置contamination=0.01识别前1%的异常样本
处理策略:对确认的异常值,优先联系数据提供方核实。无法核实的,连续变量用中位数替换,分类变量设为缺失值。
2.2 缺失值处理实战
矿物数据常见的缺失场景及应对方案:
随机缺失(MAR):
- 使用KNNImputer基于相似样本补全
- 关键参数:n_neighbors=5,weights='distance'
结构性缺失(MNAR):
- 如XRD检测未检出某矿物相导致的0值
- 建立缺失标志变量,后用0值填充
高缺失率特征:
- 对缺失>30%的特征列评估重要性
- 次要特征直接删除,关键特征用EM算法迭代估计
# 缺失值分析矩阵 msno.matrix(df) # 可视化缺失模式 print(df.isnull().sum()/len(df)) # 计算缺失比例2.3 数据标准化策略选择
不同矿物特征需要差异化的标准化方法:
| 特征类型 | 推荐方法 | 公式 | 适用场景 |
|---|---|---|---|
| 成分数据 | CLR变换 | log(x_i/g(x)) | 化学成分百分比 |
| 物理性质 | Robust Scaling | (x-median)/IQR | 含异常值的密度、硬度 |
| 光谱数据 | MinMax | (x-min)/(max-min) | 反射率等有界数据 |
| 同位素比值 | Z-score | (x-μ)/σ | δ18O等近似正态分布 |
特别注意:训练集的scaler要保存并用于测试集,避免数据泄露
3. 完整清洗流程示例
3.1 数据加载与初检
import pandas as pd raw_data = pd.read_csv('mineral_samples.csv') print(raw_data.info()) # 检查字段类型 print(raw_data.describe()) # 统计概要3.2 分步骤清洗实现
单位统一化:
# 将ppm转换为wt% df['Cu'] = df['Cu_ppm'].apply(lambda x: x/10000 if x>100 else x)异常值处理:
# 密度值物理范围修正 df.loc[df['Density']>20, 'Density'] = np.nan # 统计方法修正 df['SiO2'] = winsorize(df['SiO2'], limits=[0.01, 0.01])缺失值填补:
from sklearn.impute import IterativeImputer imputer = IterativeImputer(max_iter=10) df[['Fe','Mg']] = imputer.fit_transform(df[['Fe','Mg']])标准化处理:
from sklearn.preprocessing import PowerTransformer pt = PowerTransformer(method='yeo-johnson') df[['Density','Hardness']] = pt.fit_transform(df[['Density','Hardness']])
3.3 清洗效果验证
分布对比:
plt.figure(figsize=(12,6)) plt.subplot(121); sns.kdeplot(raw_data['SiO2'], label='原始') plt.subplot(122); sns.kdeplot(df['SiO2'], label='清洗后')相关性变化:
print(f"原始数据相关性:\n{raw_data.corr()}") print(f"清洗后相关性:\n{df.corr()}")
4. 典型问题与解决方案
4.1 成分数据的闭合效应
问题:当化学成分总和为100%时,变量之间存在虚假相关性。
解决方案:
- 使用ALR或CLR变换打破闭合
- 优先选择Isometric Log-Ratio(ILR)变换
from skbio.stats.composition import clr df[['SiO2','Al2O3']] = clr(df[['SiO2','Al2O3']])4.2 多源数据整合
问题:不同实验室检测同一批样本结果存在系统偏差。
处理方法:
- 使用标准样品数据建立校正模型
- 应用ComBat算法消除批次效应:
from combat.pycombat import pycombat df_corrected = pycombat(df, batch_col='lab_id')
4.3 类别不平衡处理
矿物样本中常见某些稀有矿物样本量极少:
| 方法 | 实现代码 | 适用场景 |
|---|---|---|
| SMOTE | from imblearn.over_sampling import SMOTE | 小样本<5% |
| 样本加权 | class_weight='balanced' | 中等不平衡 |
| 欠采样 | RandomUnderSampler() | 大样本差异 |
5. 工程化实践建议
自动化流水线设计:
from sklearn.pipeline import Pipeline prep_pipe = Pipeline([ ('imputer', IterativeImputer()), ('scaler', PowerTransformer()), ('outlier', Winsorizer()) ])元数据记录:
- 保存每个处理步骤的参数(如IQR范围)
- 记录异常值替换前后的值
版本控制:
- 使用DVC管理数据版本
- 示例命令:
dvc add data/processed/minerals_clean.csv git add data/processed/minerals_clean.csv.dvc
在实际项目中,我们通过这套流程将某铜矿样本的分类准确率从初始的72%提升到了89%。最关键的是建立了可复用的数据清洗规范,后续3个矿区的项目都直接套用此流程,平均节省了40%的前期数据处理时间。