第四章 材料数据清洗:缺失值处理、异常值检测、数据标准化与归一化、数据去重

做材料性能预测,有一句话我特别认同:垃圾进,垃圾出。你模型再牛,算法再花哨,数据本身一塌糊涂,结果就是白忙活。

我早年做过一个高温合金的蠕变寿命预测项目。当时从文献里扒了上千条数据,兴冲冲地丢进模型训练。结果呢?预测误差大得离谱。后来一查,发现有个实验组的温度数据全部少写了100度——典型的录入错误。从那以后,我养成了一个习惯:数据清洗花多少时间都不为过

这一章,我们就来聊聊材料数据清洗的四个核心环节。说白了,就是帮你把「脏数据」洗成「干净数据」。

核心观点:数据清洗不是可选项,是必选项。它占整个数据科学工作量的60%-80%。

材料数据清洗核心流程 原始材料数据 缺失值处理 删除/填充/插值 异常值检测 IQR/3σ/DBSCAN 标准化与归一化 Z-score/Min-Max 数据去重 精确/模糊去重 清洗后的高质量材料数据集

4.1 缺失值处理:别让「空」毁了你的模型

材料数据里,缺失值太常见了。比如某篇论文只报告了屈服强度,没报告延伸率;或者某个实验批次因为设备故障,部分数据没采到。你想想看,这些空值如果不管,模型训练时就会报错,或者产生偏差。

我个人习惯,先搞清楚缺失的「模式」:

  • 完全随机缺失:比如样品标签被意外弄丢。这种最省心,直接删除就行。
  • 随机缺失:比如高温性能测试中,某些样品在高温段断裂了,没测到数据。这种需要小心处理。
  • 非随机缺失:比如只有性能差的样品才没报告数据。这种最麻烦,处理不当会引入系统性偏差。

处理策略,我一般按优先级来:

  1. 直接删除:缺失比例小于5%,且样本量充足。简单粗暴,但有效。
  2. 均值/中位数填充:适用于数值型特征。我常用中位数,因为它对异常值不敏感。
  3. 前向/后向填充:适用于时间序列数据,比如连续监测的腐蚀速率。
  4. 插值法:线性插值、样条插值。适合变化平滑的数据。
  5. 模型预测填充:用其他特征训练模型来预测缺失值。这是最精细的方法,但计算量大。

我的经验:对于材料成分数据(比如合金元素含量),如果缺失值超过30%,我建议直接删除这个特征。因为强行填充会引入太多噪声,反而降低模型性能。

# 缺失值处理示例代码
import pandas as pd
import numpy as np

# 加载材料数据
df = pd.read_csv('material_data.csv')

# 查看缺失情况
print(df.isnull().sum())

# 策略1:删除缺失比例超过30%的列
df = df.dropna(thresh=len(df)*0.7, axis=1)

# 策略2:用中位数填充数值列
numeric_cols = df.select_dtypes(include=[np.number]).columns
df[numeric_cols] = df[numeric_cols].fillna(df[numeric_cols].median())

# 策略3:前向填充(适用于时间序列)
df['corrosion_rate'] = df['corrosion_rate'].fillna(method='ffill')

4.2 异常值检测:揪出那些「离谱」的数据点

异常值,说白了就是明显偏离正常范围的数据。比如某篇文献报告的「室温拉伸强度」高达5000 MPa——这明显是钛合金的数据写成了铝合金。我曾经就遇到过这种乌龙,差点把模型带偏。

检测异常值,我常用三种方法:

方法 适用场景 优点 缺点
Z-score法 数据近似正态分布 简单直观,计算快 对非正态分布效果差
IQR四分位距法 任意分布,含偏态数据 不受极端值影响 对样本量有要求
DBSCAN聚类法 高维数据,复杂分布 能发现局部异常 参数调优较麻烦

我个人最常用的是IQR法。为什么?因为材料数据往往不是完美的正态分布,比如强度数据经常是左偏的。IQR法不依赖分布假设,用起来更放心。

注意:异常值不一定是「错误」。有时候它代表一种新的材料现象。比如某个成分点突然出现了超高的韧性,这可能是个新发现。所以,检测出来后要结合领域知识判断,不要机械地删除。

# IQR法检测异常值
def detect_outliers_iqr(data, column):
    Q1 = data[column].quantile(0.25)
    Q3 = data[column].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
    return outliers

# 检测屈服强度列的异常值
outliers_yield = detect_outliers_iqr(df, 'yield_strength')
print(f"发现 {len(outliers_yield)} 个异常值")

4.3 数据标准化与归一化:让不同量纲的数据「对话」

材料数据里,特征的单位五花八门。温度是摄氏度,强度是兆帕,成分是重量百分比。你想想看,如果不做处理,模型会天然地「偏爱」数值大的特征——这显然不合理。

标准化和归一化,就是解决这个问题的。我简单解释一下区别:

  • 归一化(Min-Max Scaling):把数据缩放到[0,1]区间。适合数据有明确上下界的情况,比如元素含量(0%~100%)。
  • 标准化(Z-score):把数据变成均值为0、标准差为1的分布。适合数据没有明确边界的情况,比如强度、硬度。

我个人的选择原则:

  • 如果后续要用PCA、SVM这类对尺度敏感的算法,必须做标准化
  • 如果数据本身是比例或百分比,归一化更自然
  • 如果数据存在较多异常值,标准化比归一化更鲁棒

避坑指南:我曾经犯过一个错误——先拆分训练集和测试集,再对整个数据集做标准化。结果导致测试集的信息「泄露」到了训练集中。正确的做法是:先拆分,再用训练集的参数去标准化测试集

from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.model_selection import train_test_split

# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(
    df.drop('target', axis=1), df['target'], test_size=0.2, random_state=42
)

# 标准化:用训练集拟合,再转换测试集
scaler_std = StandardScaler()
X_train_std = scaler_std.fit_transform(X_train)
X_test_std = scaler_std.transform(X_test)  # 注意:这里用transform,不是fit_transform

# 归一化同理
scaler_mm = MinMaxScaler()
X_train_mm = scaler_mm.fit_transform(X_train)
X_test_mm = scaler_mm.transform(X_test)

4.4 数据去重:别让重复数据「虚胖」你的模型

材料数据里,重复数据很隐蔽。比如两篇论文报告了同一个合金牌号、同一个热处理工艺下的性能数据,数值几乎一样。这种重复数据会让模型「过度自信」,导致泛化能力变差。

去重分两种情况:

  • 精确去重:所有特征值完全一样。直接用drop_duplicates()就行。
  • 模糊去重:特征值不完全一样,但非常接近。比如成分差0.01%,性能差1 MPa。这种需要设定阈值来判断。

模糊去重我一般这样处理:

  1. 先对数值特征做标准化,消除量纲影响。
  2. 计算样本间的欧氏距离或余弦相似度。
  3. 设定一个阈值(比如距离小于0.05),认为它们是重复的。
  4. 保留其中一条,或者取平均值。

注意:去重前一定要确认「重复」的定义。比如同一个合金在不同温度下的性能数据,虽然成分一样,但温度不同,这不算重复。别误删了有效数据。

# 精确去重
df_clean = df.drop_duplicates()

# 模糊去重(基于成分和工艺的相似度)
from sklearn.metrics.pairwise import euclidean_distances

# 选取关键特征
features = ['Cr', 'Ni', 'Mo', 'heat_treat_temp', 'heat_treat_time']
X = df[features].values

# 计算距离矩阵
dist_matrix = euclidean_distances(X)

# 标记距离小于0.05的样本对
threshold = 0.05
duplicate_pairs = np.where(dist_matrix < threshold)
# 保留第一个出现的样本,删除后续重复
# ...(具体实现略)

小结

数据清洗这件事,说白了就是「磨刀不误砍柴工」。我见过太多人急着调模型,结果被脏数据坑得欲哭无泪。记住:干净的数据 + 简单的模型 > 脏数据 + 复杂的模型

这一章我们讲了四个核心环节:缺失值处理、异常值检测、标准化与归一化、数据去重。每个环节都有对应的策略和代码实现。你在实际项目中,可以根据数据的特点灵活组合使用。

嗯,数据洗干净了,下一步就可以放心地做特征工程和模型训练了。


专注资料整理