2. 数据导入与工程化预处理
在MATLAB机器学习工程中,数据导入与预处理往往占据整个项目60%以上的工作量。模型再先进,如果输入的数据存在缺失、异常或类别失衡,训练结果也会严重偏离预期。本节围绕“多源导入—质量诊断—清洗变换—函数封装”这条主线,讲解如何把零散的数据处理动作沉淀为可复用的工程资产。
一、多源数据导入:选对函数,保留元信息
MATLAB针对不同数据源提供了差异化的导入接口,选择依据不仅是文件格式,还包括数据规模、列类型混合程度以及是否需要保留表头语义。
- CSV/文本文件:小规模、列类型混杂时用
readtable,它返回table类型,自动识别表头并保留变量名;纯数值大矩阵用readmatrix更快;需要精细控制分隔符、缺失标记时用readtable的'Delimiter'、'TreatAsMissing'参数。 - Excel文件:
readtable('data.xlsx','Sheet','Sheet1','Range','A1:F200')可指定工作表与单元格区域,避免把说明行、汇总行误读为数据。 - 数据库:通过Database Toolbox建立连接后,用
sqlread或fetch执行查询,把结果直接读入table。连接对象应使用close及时释放,避免连接池耗尽。 - MAT文件:
load('exp.mat')会把变量注入工作区,工程中更推荐S = load('exp.mat'); T = S.T;这种显式结构体接收方式,防止变量名冲突。
导入后第一件事不是急着建模,而是做一次“数据体检”:用 summary(T) 查看每列类型、缺失数量与取值范围,用 size、class、head(T) 确认维度与类型是否符合预期。这一步能提前暴露“数值列被读成字符串”“日期列被读成文本”等常见陷阱。
二、缺失值:先判断机制,再选择策略
缺失值处理不能一律删除或一律填充。工程上先区分三种机制:完全随机缺失(MCAR)、随机缺失(MAR)和非随机缺失(MNAR)。判断依据是缺失是否与其他变量相关。例如传感器掉线导致的缺失往往与设备状态相关,属于MNAR,简单均值填充会引入偏差。
常用策略及适用场景:
- 删除:缺失比例低于5%且为MCAR时,
rmmissing(T)最直接;但要注意删除行会改变类别分布。 - 统计量填充:数值列用中位数(抗异常值),类别列用众数。
fillmissing(T,'Constant',0)或fillmissing(T,'previous')适合时间序列的前向填充。 - 模型填充:用
fitlm或fitcknn基于其他完整列预测缺失值,适合缺失比例中等且变量间相关性强的场景。
无论采用哪种方式,都建议新增一列 isMissing_xxx 标记原始缺失位置,保留“缺失本身可能是信息”的可能性。
三、异常值:检测与处置要分开决策
异常值检测常用三类方法:基于统计的Z-score(|z|>3)、基于分位数的IQR法则(超出Q1-1.5IQR或Q3+1.5IQR)、基于密度的 isoutlier 函数。MATLAB的 isoutlier(A,'quartiles') 一行即可返回逻辑索引。
检测到异常后,处置方式取决于业务含义:如果是录入错误,应修正或置为缺失;如果是真实极端事件(如故障峰值),应保留并考虑单独建模。切忌无差别用均值替换,那会抹掉真实信号。工程中常用“缩尾处理”(Winsorize):把超出阈值的值截断到边界,既保留样本量又降低极端值影响。
四、类别不平衡:从数据层与算法层双管齐下
类别不平衡在故障诊断、欺诈检测中极为常见。假设正类样本占2%,若直接训练,模型会倾向于全部预测为负类,准确率看似98%却毫无价值。处理思路分两层:
数据层:对少数类过采样(randsample 复制或SMOTE插值),对多数类欠采样。过采样易过拟合,欠采样会丢失信息,工程中常组合使用。注意:重采样只能在训练集上进行,验证集和测试集必须保持原始分布,否则评估结果失真。
算法层:使用代价敏感学习,在 fitcsvm、fitctree 等函数中通过 'ClassNames' 与 'Prior' 参数调整先验,或使用 'Weights' 给少数类更高权重。评价指标也应从准确率切换到召回率、F1或AUC。
五、封装可复用预处理函数
把上述步骤写成函数,是工程化的关键。推荐签名如下:
function [Tclean, report] = preprocessData(T, cfg)
% preprocessData 统一预处理入口
% 输入:
% T - 原始 table
% cfg - 配置结构体,字段包括:
% .missingMethod 'median'|'mode'|'drop'|'model'
% .outlierMethod 'quartiles'|'zscore'|'none'
% .outlierAction 'clip'|'remove'|'mark'
% .balanceMethod 'none'|'oversample'|'weight'
% .targetCol 目标列名
% 输出:
% Tclean - 清洗后的 table
% report - 处理报告(各列缺失数、异常数、类别分布变化)
report = struct();
% 1. 缺失值处理
switch cfg.missingMethod
case 'drop'
T = rmmissing(T);
case 'median'
T = fillmissing(T, 'constant', ...
median(T{:,:}, 'omitnan'));
case 'mode'
for v = 1:width(T)
if iscategorical(T{:,v}) || iscell(T{:,v})
T{:,v} = fillmissing(T{:,v}, 'mode');
end
end
end
% 2. 异常值处理
if ~strcmp(cfg.outlierMethod, 'none')
for v = 1:width(T)
if isnumeric(T{:,v})
idx = isoutlier(T{:,v}, cfg.outlierMethod);
if strcmp(cfg.outlierAction, 'clip')
q = quantile(T{:,v}, [0.25 0.75]);
iqr = q(2) - q(1);
T{:,v} = min(max(T{:,v}, q(1)-1.5*iqr), q(2)+1.5*iqr);
elseif strcmp(cfg.outlierAction, 'remove')
T(idx, :) = [];
end
end
end
end
% 3. 类别不平衡处理(仅训练集调用)
if strcmp(cfg.balanceMethod, 'oversample')
[X, y] = splitData(T, cfg.targetCol);
X = oversample(X, y);
T = [array2table(X), array2table(y)];
end
Tclean = T;
end
调用时只需传入配置结构体,例如:
cfg = struct('missingMethod','median', ...
'outlierMethod','quartiles', ...
'outlierAction','clip', ...
'balanceMethod','weight', ...
'targetCol','faultType');
[Tclean, report] = preprocessData(rawTable, cfg);
cfg 保存为独立的 .mat 或 .json 文件,与代码分离。这样同一套预处理逻辑可以在训练、验证、推理阶段完全一致地复用,避免“训练时做了标准化、推理时忘了”这类低级错误。
最后,report 结构体应记录每一步处理前后的样本量、缺失数、异常数、类别分布,作为实验日志的一部分。当模型效果异常时,回看这份报告往往能快速定位是数据问题还是模型问题。把预处理做成可配置、可追溯、可复用的函数,才是MATLAB机器学习工程从“脚本”走向“平台”的第一步。