材料数据多数不是单纯数组,而是带列名、单位和样本编号的表格。
import pandas as pd
df = pd.DataFrame({
"alloy": ["A", "B", "C"],
"C_wt": [0.05, 0.10, 0.20],
"Mn_wt": [0.5, 0.8, 1.0],
"hardness_HV": [160, 185, 230]
})
print(df.head())
print(df.info())
print(df.describe())
Pandas 的核心优势:列名、索引、缺失值处理、分组统计。
一个 DataFrame 可以理解为带标签的二维数据表。
| 组成 | 含义 | 示例 |
|---|---|---|
| 行索引 | 样本编号 | alloy_001 |
| 列名 | 变量名 | C_wt、HV |
| 值 | 具体数据 | 0.12、260 |
| 数据类型 | int、float、object | 数值、字符串 |
| 元数据 | 单位、来源 | wt.%、K、HV |
材料数据表的第一原则:
一行一个样本,一列一个变量,一个单元格一个值。
import pandas as pd
# 读取CSV文件
df_csv = pd.read_csv("data/raw/alloy_data.csv")
# 读取Excel文件
df_xlsx = pd.read_excel("data/raw/alloy_data.xlsx", sheet_name="Sheet1")
# 查看数据结构
print(df_csv.shape)
print(df_csv.columns)
print(df_csv.head())
读取后必须先检查:
# 选择单列
carbon = df["C_wt"]
# 选择多列
features = df[["C_wt", "Mn_wt", "hardness_HV"]]
# 条件筛选:选取碳含量大于0.1 wt.%的样本
high_c = df[df["C_wt"] > 0.10]
# 多条件筛选
selected = df[(df["C_wt"] > 0.10) & (df["hardness_HV"] > 200)]
筛选的本质是构造布尔掩码:
m=[m1,m2,…,mn],mi∈{True,False}
材料数据预处理经常需要构造派生特征。
# 简化碳当量公式,仅用于教学示例
df["CE"] = df["C_wt"] + df["Mn_wt"] / 6
# 工艺复合变量
df["heat_input"] = df["power_W"] / df["scan_speed_mm_s"]
# 取对数变换,处理跨度大的变量
import numpy as np
df["log_cooling_rate"] = np.log10(df["cooling_rate"])
派生特征应有物理含义,不能只追求数量。
# 按热处理状态分组,计算平均硬度和标准差
summary = df.groupby("heat_treatment")["hardness_HV"].agg(["mean", "std", "count"])
print(summary)
# 按合金体系和工艺路线双重分组
summary2 = df.groupby(["alloy_system", "process"])["strength_MPa"].mean()
材料场景:
Matplotlib 是 Python 基础绘图库。
import matplotlib.pyplot as plt
plt.figure(figsize=(5, 4))
plt.scatter(df["C_wt"], df["hardness_HV"])
plt.xlabel("C / wt.%")
plt.ylabel("Hardness / HV")
plt.title("Effect of carbon on hardness")
plt.tight_layout()
plt.show()
图形不是装饰,而是数据诊断工具:
Matplotlib 图形由两层组成:
Figure:整张画布Axes:具体绘图区fig, ax = plt.subplots(figsize=(5, 4))
ax.scatter(df["C_wt"], df["hardness_HV"])
ax.set_xlabel("C / wt.%")
ax.set_ylabel("Hardness / HV")
ax.set_title("C-Hardness relation")
plt.show()
推荐使用 fig, ax = plt.subplots(),便于后续精细控制。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Pandas读取数据
df = pd.read_csv("alloy_data.csv")
# NumPy进行数值变换
x = df["cooling_rate"].to_numpy()
df["log_rate"] = np.log10(x)
# Matplotlib绘图
fig, ax = plt.subplots()
ax.scatter(df["log_rate"], df["hardness_HV"])
ax.set_xlabel("log10(cooling rate)")
ax.set_ylabel("Hardness / HV")
plt.show()
工作流:读取 → 变换 → 可视化 → 诊断。
本节应形成三个基本能力:
三者分工:
数值计算→表格管理→图形诊断
下一节进入材料数据类型:材料数据为什么比普通表格数据更复杂?
材料数据不是单一类型的数据,而是多源、多尺度、多模态数据。
本节将材料数据分为六类:
核心问题:
如何把不同来源、不同尺度、不同格式的材料信息组织成可分析的数据结构?
材料科学常用关系链:
成分+工艺→组织结构→性能
进一步可写为:
y=f(c,p,m)
其中:
AI 的任务是从数据中近似未知映射 f。
成分数据通常包括:
成分数据的常见问题:
成分数据看似简单,但最容易产生单位和归一化错误。
| sample_id | Fe_wt | C_wt | Mn_wt | Si_wt | Cr_wt | Ni_wt |
|---|---|---|---|---|---|---|
| S001 | 98.20 | 0.20 | 1.10 | 0.30 | 0.10 | 0.10 |
| S002 | 97.80 | 0.35 | 1.20 | 0.40 | 0.15 | 0.10 |
| S003 | 96.50 | 0.10 | 0.80 | 0.20 | 1.50 | 0.90 |
推荐规则:
若成分总和不是 1 或 100%,常需要归一化:
c~j=∑k=1dckcj
Python 示例:
import pandas as pd
cols = ["Fe_wt", "C_wt", "Mn_wt", "Si_wt"]
row_sum = df[cols].sum(axis=1)
df_norm = df[cols].div(row_sum, axis=0)
注意:归一化前应判断是否需要保留杂质、空位或未测元素。
质量分数到原子分数的基本关系:
xj=∑k=1dwk/Mkwj/Mj
其中:
该转换在相图计算、热力学建模和第一性原理数据整合中很常见。
import pandas as pd
comp_wt = pd.Series({"Fe": 98.0, "C": 0.2, "Mn": 1.5, "Si": 0.3})
M = pd.Series({"Fe": 55.845, "C": 12.011, "Mn": 54.938, "Si": 28.085})
moles = comp_wt / M
comp_at = moles / moles.sum()
print(comp_at * 100)
结果解释:
工艺数据描述材料如何被制备或加工:
工艺数据通常是材料数据中最不规范的一类。
在增材制造中,常用体积能量密度作为简化工艺指标:
E=vhtP
其中:
注意:E 是简化指标,不等价于真实热输入场。真实组织演化还受热传导、熔池流动和重复热循环影响。
# P: W, v: mm/s, h: mm, t: mm
# E: J/mm^3
df["energy_density"] = (
df["laser_power_W"] /
(df["scan_speed_mm_s"] * df["hatch_spacing_mm"] * df["layer_thickness_mm"])
)
# 焊接线能量,单位 J/mm
df["line_energy"] = df["voltage_V"] * df["current_A"] / df["welding_speed_mm_s"]
派生特征必须记录公式和单位,否则难以复现。
显微组织数据可以来自:
组织描述符示例:
| sample_id | grain_size_um | phase_alpha_frac | porosity_pct | texture_index |
|---|---|---|---|---|
| S001 | 12.5 | 0.32 | 0.20 | 1.8 |
| S002 | 8.4 | 0.45 | 0.35 | 2.3 |
| S003 | 20.1 | 0.18 | 0.10 | 1.2 |
组织数据的主要难点:
性能数据可以是标量,也可以是曲线或多目标。
常见性能:
关键问题:性能不是材料固有常数,往往依赖测试条件。
同一材料的性能数据应同时记录:
| 性能 | 必要条件 | 说明 |
|---|---|---|
| 硬度 | 载荷、保载时间、测试位置 | HV0.2 与 HV10 不可混用 |
| 拉伸强度 | 试样尺寸、应变速率、温度 | 室温与高温不可直接合并 |
| 疲劳寿命 | 应力比、频率、环境 | 寿命常需对数处理 |
| 腐蚀性能 | 溶液、温度、扫描速率 | 测试体系决定结果 |
缺少测试条件的性能数据不适合直接作为高可信标签。
显微组织图像可表示为张量:
I∈RH×W×C
其中:
图像数据处理要点:
图像数据不宜直接塞进 Excel,应采用“文件路径 + 标签表”的方式:
| image_id | file_path | alloy | process | label |
|---|---|---|---|---|
| I001 | images/S001_01.png | Fe-C | casting | dendrite |
| I002 | images/S001_02.png | Fe-C | casting | dendrite |
| I003 | images/S002_01.png | Ti alloy | AM | pore |
优点:
谱图数据包括:
谱图可视为一维信号:
s=[I(x1),I(x2),…,I(xm)]
其中 x 可以是角度、能量或波数。
谱图进入机器学习前通常需要:
注意:过度平滑可能抹掉真实峰信息;背景扣除方法应固定并记录。
多源数据整合的关键是样本主键:
sample_id
├── composition.csv
├── process.csv
├── microstructure.csv
├── property.csv
├── images/*.png
└── spectra/*.csv
推荐原则:
sample_idsample_id 合并comp = pd.read_csv("composition.csv")
proc = pd.read_csv("process.csv")
prop = pd.read_csv("property.csv")
# 按 sample_id 合并
alloy_data = comp.merge(proc, on="sample_id", how="inner")
alloy_data = alloy_data.merge(prop, on="sample_id", how="inner")
print(alloy_data.shape)
print(alloy_data.head())
合并前必须检查:
sample_id 是否唯一inner、left、outer 合并方式是否合理材料数据的复杂性来自:
下一节讨论:这些数据进入模型前必须如何清洗。
数据清洗是材料 AI 建模前最关键的环节。
本节重点:
核心观点:
清洗不是“美化数据”,而是把数据问题显式化、可追踪化。
| 问题 | 表现 | 后果 |
|---|---|---|
| 缺失值 | 空白、NA、--、未检出 | 模型无法训练或产生偏差 |
| 重复值 | 同一样本重复录入 | 过度代表某类样本 |
| 单位混乱 | mm/s 与 m/s 混用 | 数值量级错误 |
| 类型错误 | 数值读成字符串 | 统计计算失败 |
| 异常值 | 远离总体分布 | 扭曲模型参数 |
| 数据泄漏 | 测试集信息进入训练 | 评估结果虚高 |
材料数据清洗必须同时依赖统计判断和材料知识。
推荐流程:
读取原始数据
↓
检查行列、字段、单位和数据类型
↓
处理重复样本和明显录入错误
↓
识别缺失值并分析缺失机制
↓
识别异常值并区分错误与真实极端样本
↓
标准化、编码和特征构造
↓
保存处理后数据并记录处理日志
注意:处理顺序应固定,并能由代码重新运行。
import pandas as pd
df = pd.read_csv("data/raw/alloy_data.csv")
print(df.shape)
print(df.columns)
print(df.head())
print(df.info())
print(df.describe(include="all"))
重点观察:
不规范列名会导致代码难以维护。
# 原始列名可能为:"C (wt%)", "Cooling Rate/Ks-1"
df.columns = (
df.columns
.str.strip()
.str.replace(" ", "_")
.str.replace("(", "", regex=False)
.str.replace(")", "", regex=False)
.str.lower()
)
print(df.columns)
推荐列名:
c_wtmn_wtcooling_rate_k_shardness_hv# 查看每列类型
print(df.dtypes)
# 将字符串数值转为浮点数,错误值转为 NaN
df["c_wt"] = pd.to_numeric(df["c_wt"], errors="coerce")
# 类别变量统一为字符串
df["process"] = df["process"].astype("string")
常见问题:
"0.25" 被读成字符串"<0.01" 无法直接转数值"未测"、"--"、"N/A" 含义不同# 检查完全重复行
print(df.duplicated().sum())
# 根据 sample_id 检查重复样本
print(df["sample_id"].duplicated().sum())
# 查看重复记录
duplicates = df[df["sample_id"].duplicated(keep=False)]
print(duplicates)
处理原则:
单位错误是材料数据中最严重的问题之一。
示例:扫描速度统一为 mm/s。
# 若原始数据有速度值和单位两列
def convert_speed(row):
if row["speed_unit"] == "m/s":
return row["speed"] * 1000
elif row["speed_unit"] == "mm/s":
return row["speed"]
else:
return None
df["scan_speed_mm_s"] = df.apply(convert_speed, axis=1)
原则:单位转换必须写入代码,不建议手工改 Excel。
# 每列缺失值数量
missing_count = df.isna().sum()
# 每列缺失率
missing_ratio = df.isna().mean()
summary = pd.DataFrame({
"missing_count": missing_count,
"missing_ratio": missing_ratio
})
print(summary.sort_values("missing_ratio", ascending=False))
缺失率定义:
rj=nnmissing,j
其中 rj 为第 j 列的缺失比例。
缺失机制影响处理方法:
| 缺失机制 | 含义 | 示例 | 处理思路 |
|---|---|---|---|
| 完全随机缺失 | 与变量无关 | 仪器偶然故障 | 删除或插补 |
| 随机缺失 | 与观测变量有关 | 高温样本更易漏记 | 建模插补 |
| 非随机缺失 | 与缺失值自身有关 | 低于检测限未报告 | 不能简单插补 |
材料数据中“未检出”不等于 0,也不等于普通缺失。
删除适合缺失少且不影响样本代表性的情况。
# 删除目标值缺失的样本
df1 = df.dropna(subset=["hardness_hv"])
# 删除缺失率过高的列
threshold = 0.5
cols_keep = df.columns[df.isna().mean() < threshold]
df2 = df[cols_keep]
风险:
常见插补方法:
x~ij={xij,μj,若观测到若缺失且采用均值插补
Python 示例:
from sklearn.impute import SimpleImputer
num_cols = ["c_wt", "mn_wt", "cooling_rate_k_s"]
imputer = SimpleImputer(strategy="median")
X_imp = imputer.fit_transform(df[num_cols])
中位数插补比均值插补对异常值更稳健。
若不同合金体系分布差异很大,应考虑分组插补。
# 按合金体系用组内中位数插补硬度
med = df.groupby("alloy_system")["hardness_hv"].transform("median")
df["hardness_hv_filled"] = df["hardness_hv"].fillna(med)
适用场景:
注意:不能使用目标变量未来信息进行插补。
缺失本身可能包含信息。例如“某元素未检出”可能代表含量低。
# 创建缺失指示变量
df["o_missing"] = df["o_wt"].isna().astype(int)
# 再对原始列插补
df["o_wt"] = df["o_wt"].fillna(0.0)
数学表示:
mij={1,0,若 xij 缺失若 xij 被观测
缺失指示变量可帮助模型识别“未测”和“真实为零”的差异。
异常值有两种完全不同的含义:
数据错误
真实极端样本
材料数据中不能只靠统计规则删除异常值,必须结合物理和实验记录判断。
Z-score 定义为:
zi=σxi−μ
常用规则:
∣zi∣>3⇒可能为异常值
Python 示例:
x = df["hardness_hv"]
z = (x - x.mean()) / x.std()
outliers = df[z.abs() > 3]
print(outliers)
缺点:均值和标准差本身会受异常值影响。
四分位距:
IQR=Q3−Q1
异常值判据:
x<Q1−1.5IQR或x>Q3+1.5IQR
Python 示例:
x = df["hardness_hv"]
q1 = x.quantile(0.25)
q3 = x.quantile(0.75)
iqr = q3 - q1
mask = (x < q1 - 1.5 * iqr) | (x > q3 + 1.5 * iqr)
print(df[mask])
多变量异常值不一定在单个变量上极端。
马氏距离:
DM2=(x−μ)TΣ−1(x−μ)
其中:
意义:考虑变量相关性后的距离。
材料场景:某个样本的 C 和 Mn 单独看正常,但组合关系不符合合金设计规律。
import numpy as np
cols = ["c_wt", "mn_wt", "si_wt", "hardness_hv"]
X = df[cols].dropna().to_numpy()
mu = X.mean(axis=0)
Sigma = np.cov(X, rowvar=False)
Sigma_inv = np.linalg.pinv(Sigma)
diff = X - mu
D2 = np.sum(diff @ Sigma_inv * diff, axis=1)
print(D2)
注意:样本量很小时协方差矩阵估计不稳定。
| 方式 | 适用情况 | 风险 |
|---|---|---|
| 回查更正 | 明确录入或单位错误 | 需要原始记录 |
| 删除 | 明确为错误数据 | 可能删除真实机制 |
| 截尾/Winsorize | 极端值影响统计量 | 改变真实分布 |
| 保留并标注 | 可能是真实极端样本 | 模型可能受影响 |
推荐实践:
标准化公式:
xij′=σjxij−μj
其中:
适用场景:
随机森林等树模型通常对尺度不敏感。
Min-Max 归一化:
xij′=xj,max−xj,minxij−xj,min
Python 示例:
from sklearn.preprocessing import StandardScaler, MinMaxScaler
cols = ["c_wt", "mn_wt", "cooling_rate_k_s"]
scaler = StandardScaler()
X_std = scaler.fit_transform(df[cols])
minmax = MinMaxScaler()
X_mm = minmax.fit_transform(df[cols])
注意:fit 只能在训练集上进行,不能在全数据上进行。
数据泄漏指测试集信息以某种方式进入训练过程。
典型错误:
正确原则:
任何需要“学习参数”的预处理步骤,只能在训练集上
fit。
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
X = df[["c_wt", "mn_wt", "cooling_rate_k_s"]]
y = df["hardness_hv"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=0
)
pipe = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler())
])
X_train_p = pipe.fit_transform(X_train)
X_test_p = pipe.transform(X_test)
数据清洗应形成可复现流程:
下一节:用统计图形检查清洗后的材料数据。
可视化与统计描述用于回答:
本节重点:
常用统计量:
| 统计量 | 公式或含义 | 用途 |
|---|---|---|
| 均值 | xˉ | 集中趋势 |
| 中位数 | Q2 | 抗异常值 |
| 标准差 | s | 离散程度 |
| 四分位数 | Q1,Q3 | 分布范围 |
| 偏度 | 分布不对称性 | 判断长尾 |
| 相关系数 | r | 变量关系 |
描述统计不是结论,而是建模前的数据诊断。
# 数值列统计
print(df.describe())
# 指定列统计
cols = ["c_wt", "mn_wt", "cooling_rate_k_s", "hardness_hv"]
print(df[cols].describe())
# 类别列频数
print(df["process"].value_counts())
观察重点:
count 是否一致min 和 max 是否物理合理mean 与 50% 差异是否很大fig, ax = plt.subplots(figsize=(5, 4))
ax.hist(df["hardness_hv"].dropna(), bins=15, edgecolor="black")
ax.set_xlabel("Hardness / HV")
ax.set_ylabel("Count")
ax.set_title("Distribution of hardness")
plt.tight_layout()
plt.show()
直方图用于检查:
fig, ax = plt.subplots(figsize=(5, 4))
df.boxplot(column="hardness_hv", by="process", ax=ax)
ax.set_xlabel("Process")
ax.set_ylabel("Hardness / HV")
plt.suptitle("")
plt.tight_layout()
plt.show()
箱线图包含:
适合比较不同工艺路线的性能分布。
fig, ax = plt.subplots(figsize=(5, 4))
ax.scatter(df["cooling_rate_k_s"], df["hardness_hv"])
ax.set_xscale("log")
ax.set_xlabel("Cooling rate / K s$^{-1}$")
ax.set_ylabel("Hardness / HV")
plt.tight_layout()
plt.show()
散点图可以判断:
Pearson 相关系数:
rxy=∑i=1n(xi−xˉ)2
取值范围:
−1≤rxy≤1
解释:
cols = ["c_wt", "mn_wt", "si_wt", "cooling_rate_k_s", "hardness_hv"]
corr = df[cols].corr(method="pearson")
print(corr)
fig, ax = plt.subplots(figsize=(5, 4))
im = ax.imshow(corr, vmin=-1, vmax=1)
ax.set_xticks(range(len(cols)), labels=cols, rotation=45, ha="right")
ax.set_yticks(range(len(cols)), labels=cols)
fig.colorbar(im, ax=ax)
plt.tight_layout()
plt.show()
相关性不能直接解释为因果关系。
summary = df.groupby("process")["hardness_hv"].agg(["mean", "std", "count"])
summary["sem"] = summary["std"] / summary["count"]**0.5
fig, ax = plt.subplots(figsize=(5, 4))
ax.errorbar(summary.index, summary["mean"], yerr=summary["sem"], fmt="o")
ax.set_xlabel("Process")
ax.set_ylabel("Hardness / HV")
plt.tight_layout()
plt.show()
标准误:
SEM=n
对于三元合金 A-B-C:
cA+cB+cC=1
三元图用于展示成分空间中的性能分布。
课堂中可先用二维投影近似理解:
fig, ax = plt.subplots(figsize=(5, 4))
sc = ax.scatter(df["cr_wt"], df["ni_wt"], c=df["hardness_hv"])
ax.set_xlabel("Cr / wt.%")
ax.set_ylabel("Ni / wt.%")
fig.colorbar(sc, ax=ax, label="Hardness / HV")
plt.show()
颜色表示目标性能或相组成。
增材制造中常用二维工艺图观察质量区间:
fig, ax = plt.subplots(figsize=(5, 4))
sc = ax.scatter(
df["laser_power_W"],
df["scan_speed_mm_s"],
c=df["relative_density_pct"]
)
ax.set_xlabel("Laser power / W")
ax.set_ylabel("Scan speed / mm s$^{-1}$")
fig.colorbar(sc, ax=ax, label="Relative density / %")
plt.show()
可用于识别:
import matplotlib.pyplot as plt
from matplotlib.image import imread
img = imread("images/microstructure_001.png")
fig, ax = plt.subplots(figsize=(5, 4))
ax.imshow(img, cmap="gray")
ax.axis("off")
ax.set_title("Microstructure image")
plt.show()
图像可视化注意事项:
spec = pd.read_csv("spectra/xrd_s001.csv")
fig, ax = plt.subplots(figsize=(5, 4))
ax.plot(spec["two_theta"], spec["intensity"])
ax.set_xlabel(r"$2\theta$ / degree")
ax.set_ylabel("Intensity")
ax.set_title("XRD pattern")
plt.tight_layout()
plt.show()
谱图诊断重点:
材料论文和报告中的图形应满足:
图形是证据链的一部分,而不是最后的装饰。
可视化与统计描述的目标:
常用图形:
下一节:数据看起来合理,是否就意味着模型可靠?
本节讨论更高层次的问题:
核心观点:
模型预测能力的上限,往往由数据质量决定,而不是由算法名称决定。
| 维度 | 含义 | 材料数据示例 |
|---|---|---|
| 准确性 | 数值是否正确 | 成分检测误差 |
| 完整性 | 关键字段是否缺失 | 热处理时间缺失 |
| 一致性 | 单位和标准是否统一 | HV与HRC混用 |
| 代表性 | 样本是否覆盖目标空间 | 只包含高性能样本 |
| 可追溯性 | 来源是否清楚 | 文献数据缺少测试条件 |
| 可复现性 | 能否重复生成数据集 | 清洗步骤无代码 |
数据质量应在建模前评估,而不是模型失败后补救。
选择偏差指样本不是目标总体的代表。
材料案例:
后果:
测量偏差来自仪器、方法或标准差异。
示例:
处理方式:
标签偏差指目标值或类别标签存在系统性偏差。
显微组织识别中的常见问题:
建议:
材料数据常见状态:
n≪d
其中:
问题:
解决思路:
模型通常只在训练数据覆盖范围内可靠。
若训练数据满足:
x∈Ωtrain
而预测点位于:
x∗otinΩtrain
则属于外推预测。
材料案例:
features = ["c_wt", "mn_wt", "cooling_rate_k_s"]
for col in features:
print(col, df[col].min(), df[col].max())
# 检查一个新样本是否超出训练范围
new_sample = {"c_wt": 0.8, "mn_wt": 1.0, "cooling_rate_k_s": 1000}
for col, value in new_sample.items():
low, high = df[col].min(), df[col].max()
print(col, low <= value <= high)
超出范围不代表不能预测,但必须标注为低可信外推。
材料数据划分不能只随机切分。
高风险情形:
建议:
from sklearn.model_selection import GroupShuffleSplit
X = df[["c_wt", "mn_wt", "cooling_rate_k_s"]]
y = df["hardness_hv"]
groups = df["sample_id"]
splitter = GroupShuffleSplit(test_size=0.2, random_state=0)
train_idx, test_idx = next(splitter.split(X, y, groups=groups))
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
分组划分可以降低重复样本造成的虚高评估。
常用回归误差:
MAE=n1i=1∑n∣yi−y^i∣
RMSE=n1i=1∑n(yi−y^i)2
如果标签测量误差本身约为 ±20HV,则模型 MAE 为 5HV 可能不可信。
模型误差不能脱离实验误差解释。
建议为每个数据集建立处理日志:
| 项目 | 内容 |
|---|---|
| 数据来源 | 实验、模拟、文献、数据库 |
| 原始文件 | 文件名、版本、日期 |
| 清洗规则 | 删除哪些样本,为什么删除 |
| 单位转换 | 转换公式和目标单位 |
| 缺失处理 | 插补方法和缺失比例 |
| 异常处理 | 异常判据和处理结果 |
| 输出文件 | 文件名、生成脚本、生成日期 |
处理日志是数据可靠性的证据。
def preprocess_alloy_data(df):
"""清洗材料数据表,返回处理后的DataFrame。"""
df = df.copy()
# 1. 统一列名
df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
# 2. 数值列转换
num_cols = ["c_wt", "mn_wt", "cooling_rate_k_s", "hardness_hv"]
for col in num_cols:
df[col] = pd.to_numeric(df[col], errors="coerce")
# 3. 删除目标值缺失样本
df = df.dropna(subset=["hardness_hv"])
# 4. 构造派生特征
df["ce"] = df["c_wt"] + df["mn_wt"] / 6
return df
建模前建议逐项检查:
通过清单化方式降低人为遗漏。
模型可靠性来自三方面:
数据可靠
划分可靠
解释可靠
下一步:进行本章综合案例与编程作业。
假设已有一份合金硬度数据,字段包括:
sample_id:样本编号C_wt、Mn_wt、Si_wt:成分cooling_rate:冷却速率process:制备工艺hardness_HV:硬度目标:
将原始数据清洗为可用于机器学习建模的数据表,并输出基本统计图。
import pandas as pd
raw_path = "data/raw/alloy_hardness.csv"
df = pd.read_csv(raw_path)
print("shape:", df.shape)
print("columns:", df.columns.tolist())
print(df.head())
print(df.info())
print(df.isna().mean().sort_values(ascending=False))
检查目的:
df.columns = (
df.columns
.str.strip()
.str.replace(" ", "_")
.str.lower()
)
num_cols = ["c_wt", "mn_wt", "si_wt", "cooling_rate", "hardness_hv"]
for col in num_cols:
df[col] = pd.to_numeric(df[col], errors="coerce")
# 类别变量规范化
df["process"] = df["process"].str.strip().str.lower()
该步骤使后续代码不依赖原始表格中的偶然格式。
comp_cols = ["c_wt", "mn_wt", "si_wt"]
# 检查负值
for col in comp_cols:
print(col, (df[col] < 0).sum())
# 简化检查:合金元素总量不应超过100 wt.%
df["known_sum_wt"] = df[comp_cols].sum(axis=1)
print(df[df["known_sum_wt"] > 100])
若包含所有元素,可进一步检查:
j∑cj≈100%
# 删除目标值缺失样本
df = df.dropna(subset=["hardness_hv"])
# 数值特征用中位数插补
features = ["c_wt", "mn_wt", "si_wt", "cooling_rate"]
for col in features:
df[col + "_missing"] = df[col].isna().astype(int)
df[col] = df[col].fillna(df[col].median())
保留缺失指示变量可以记录原始缺失信息。
x = df["hardness_hv"]
q1 = x.quantile(0.25)
q3 = x.quantile(0.75)
iqr = q3 - q1
low = q1 - 1.5 * iqr
high = q3 + 1.5 * iqr
outlier_mask = (x < low) | (x > high)
print(df.loc[outlier_mask, ["sample_id", "hardness_hv", "process"]])
输出后应回查原始实验记录,而不是直接删除。
import numpy as np
# 碳当量教学示例
df["ce"] = df["c_wt"] + df["mn_wt"] / 6
# 冷却速率跨度大,取对数
df["log_cooling_rate"] = np.log10(df["cooling_rate"])
# 保存处理后数据
df.to_csv("data/processed/alloy_hardness_clean.csv", index=False)
保存时建议同时保存处理脚本和数据说明文件。
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(5, 4))
ax.scatter(df["log_cooling_rate"], df["hardness_hv"])
ax.set_xlabel("log10(cooling rate)")
ax.set_ylabel("Hardness / HV")
ax.set_title("Hardness vs cooling rate")
plt.tight_layout()
plt.savefig("figures/hardness_vs_cooling_rate.png", dpi=300)
plt.show()
图像文件应可由代码重复生成。
本章形成完整的数据预处理链条:
Python环境
↓
NumPy数组、Pandas表格、Matplotlib图形
↓
材料数据类型识别
↓
缺失值、异常值、单位和类型处理
↓
统计描述与可视化诊断
↓
数据质量、偏差和模型可靠性评估
核心能力:把材料数据从“能看懂”变成“能建模”。
ndarray:高效数值数组DataFrame:带列名和索引的二维数据表题目:合金硬度数据预处理与可视化
请自行构造或整理一个至少包含 20 个样本的数据表,字段包括:
sample_idc_wt、mn_wt、si_wtcooling_rate、heat_treatment_temphardness_hv要求:完成缺失值检查、异常值识别、派生特征构造和至少 3 张图。
提交内容:
raw_data.csvpreprocess_homework.py 或 Notebookclean_data.csv必须包含:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 1. 读取数据
df = pd.read_csv("raw_data.csv")
# 2. 检查缺失值
print(df.isna().mean())
# 3. 数值列转换与插补
num_cols = ["c_wt", "mn_wt", "si_wt", "cooling_rate", "hardness_hv"]
for col in num_cols:
df[col] = pd.to_numeric(df[col], errors="coerce")
df[col] = df[col].fillna(df[col].median())
# 4. 构造派生特征
df["ce"] = df["c_wt"] + df["mn_wt"] / 6
# 5. 保存结果
df.to_csv("clean_data.csv", index=False)
| 项目 | 分值 | 要求 |
|---|---|---|
| 数据结构 | 20 | 字段清晰,单位明确,一行一个样本 |
| 缺失与异常处理 | 25 | 有统计、有方法、有说明 |
| 派生特征 | 15 | 具有材料意义,不是机械堆砌 |
| 可视化 | 20 | 图形清楚,坐标轴和单位完整 |
| 可靠性讨论 | 20 | 能指出数据偏差、样本量或外推风险 |
重点不是代码复杂,而是数据处理逻辑清楚。