2025五一赛B题矿山数据处理:从预处理到建模的完整链路与避坑指南
简介本资源为2025年五一数学建模竞赛B题「矿山监测数据的高效处理与建模优化研究」的完整参赛作品包含完整论文与配套代码面向具备一定数学建模基础、关注矿山监测数据处理的科研人员与工程师。作品综合运用BP神经网络、主成分分析、卡尔曼滤波与贝叶斯优化等方法围绕数据拟合、降维压缩、去噪建模、超参数自适应调整及高维数据重构五个问题给出完整求解方案测试集拟合优度最高达0.9870。资源包共1个PDF文件大小约3.23MB内容涵盖问题背景重述、模型假设、各问题分析与求解过程、误差检验及算法复杂度讨论结构完整、逻辑清晰。目前已有306人学习下载博主自述为个人参赛作品保底二等奖水平适合作为数学建模竞赛的参考范例也可为金融、气象、交通等领域的时序数据处理提供迁移思路。1. 矿山数据处理赛题的底层逻辑为什么它成了2025年五一赛B题的硬骨头2025年五一赛B题把场景放在矿山表面看是道数学建模题实际是一道披着行业外衣的数据处理综合题。矿山数据有几个绕不开的特点传感器多、采样频率不统一、缺失值和异常值混在一起、量纲差异大还经常出现时间戳对不齐的情况。很多队伍拿到题第一反应是套预测模型结果在数据清洗阶段就翻车了。这道题真正考的是你能不能把脏数据变成可建模的干净矩阵再用合理的模型给出可解释的结论。适合谁看准备参加数学建模竞赛的学生、刚接触工业时序数据的工程师、以及想补上数据预处理到建模这条链路的人。下面我按自己带队的实际路径把这道题从读题到出论文的完整流程拆开讲。2. 矿山数据预处理从原始表格到可建模矩阵的完整链路矿山数据处理的第一个分水岭就在预处理。我见过太多队伍直接把原始Excel丢进模型最后结果惨不忍睹。这一章把预处理拆成缺失值处理、异常值检测、多源对齐三个环节每个环节给出可复现的代码和参数选择理由。2.1 缺失值的三类处理策略与选择依据矿山传感器数据缺失通常分三种随机缺失、连续块缺失、整列缺失。随机缺失用插值连续块缺失要判断是否设备停机整列缺失直接考虑剔除该特征。我一般先用pandas做缺失分布统计再决定策略。import pandas as pd import numpy as np # 读取原始矿山数据假设第一列是时间戳 df pd.read_csv(mine_data.csv, parse_dates[timestamp]) df df.set_index(timestamp).sort_index() # 统计每列缺失率和最大连续缺失长度 def missing_profile(series): mask series.isna() missing_rate mask.mean() # 计算最大连续缺失长度 groups (~mask).cumsum() max_consecutive mask.groupby(groups).sum().max() if mask.any() else 0 return pd.Series({missing_rate: missing_rate, max_consecutive: max_consecutive}) profile df.apply(missing_profile) print(profile.sort_values(missing_rate, ascendingFalse))这段代码输出每列的缺失率和最大连续缺失长度。判断规则缺失率超过40%且最大连续缺失超过50个采样点的列直接剔除缺失率在10%到40%之间的列用线性插值加前后向填充组合缺失率低于10%的列用三次样条插值。参数上max_consecutive的阈值我一般设50对应高频传感器约10分钟的停机超过这个长度插值就是编数据了。2.2 异常值检测3σ和IQR到底该用哪个异常值检测没有万能方法。矿山数据里振动传感器适合IQR因为分布偏态温度压力类适合3σ接近正态。我的做法是两条路都跑取交集作为高置信异常取并集作为疑似异常。from scipy import stats def detect_outliers(series, methodboth): clean series.dropna() # 3σ方法 z np.abs(stats.zscore(clean)) sigma_out clean.index[z 3] # IQR方法 q1, q3 clean.quantile([0.25, 0.75]) iqr q3 - q1 lower, upper q1 - 1.5 * iqr, q3 1.5 * iqr iqr_out clean.index[(clean lower) | (clean upper)] if method both: high_conf sigma_out.intersection(iqr_out) suspect sigma_out.union(iqr_out) return high_conf, suspect return sigma_out if method sigma else iqr_out # 对每个数值列检测 for col in df.select_dtypes(include[np.number]).columns: high, suspect detect_outliers(df[col]) print(f{col}: 高置信异常{len(high)}个, 疑似异常{len(suspect)}个)高置信异常直接置为NaN走插值疑似异常保留但打标记在后续建模时作为特征输入。这样做的理由是矿山数据里有些异常其实是真实工况突变直接删会丢信息。参数上3σ的阈值可以放宽到3.5IQR的1.5倍可以调到2.0取决于你对数据质量的容忍度。2.3 多源时间对齐重采样与滑动窗口的配合矿山数据来自不同设备采样频率从1秒到1小时不等。对齐的核心是统一到同一个时间网格。我一般选中间频率作为基准比如1分钟高频降采样用均值低频升采样用插值。# 统一重采样到1分钟 df_resampled df.resample(1min).agg({ vibration: mean, # 高频振动取均值 temperature: mean, pressure: mean, production: sum # 产量类累加 }) # 对重采样后仍缺失的做插值 df_resampled df_resampled.interpolate(methodtime, limit5) # 构建滑动窗口特征窗口大小60步长1 def make_windows(data, window60, step1): windows [] for i in range(0, len(data) - window, step): windows.append(data.iloc[i:iwindow].values) return np.array(windows) feature_cols [vibration, temperature, pressure] X make_windows(df_resampled[feature_cols]) print(f窗口数据形状: {X.shape})重采样时聚合函数的选择很关键振动、温度、压力用均值产量、能耗用求和状态标志用众数或最后一个值。插值的limit5表示连续缺失超过5个点就不插了避免过度平滑。滑动窗口的60对应1小时历史这个参数要根据预测目标调整预测未来10分钟用60预测未来1小时用180。3. 特征工程与模型选型让矿山数据开口说话预处理完只是拿到干净数据真正决定论文质量的是特征工程和模型选择。这一章讲怎么从时序数据里挖出有物理意义的特征以及为什么这道题我推荐树模型加时序模型的组合。3.1 时域特征与频域特征的提取清单矿山数据的特征分三类统计特征、时域特征、频域特征。统计特征就是均值方差那些时域特征包括过零率、峰值因子频域特征靠FFT。我一般提取以下清单特征类型具体特征计算方式适用场景统计特征均值、标准差、偏度、峰度pandas内置所有数值列时域特征均方根、峰值因子、裕度因子自定义函数振动信号频域特征主频、频谱熵、频带能量FFT后计算振动、电流趋势特征滑动斜率、差分均值线性回归温度、压力from scipy.fft import fft from scipy.stats import skew, kurtosis def extract_features(window_data, fs1.0): window_data: (window_size, n_features) feats [] for i in range(window_data.shape[1]): sig window_data[:, i] # 统计特征 feats.extend([np.mean(sig), np.std(sig), skew(sig), kurtosis(sig)]) # 时域特征 rms np.sqrt(np.mean(sig**2)) peak np.max(np.abs(sig)) crest peak / (rms 1e-8) feats.extend([rms, peak, crest]) # 频域特征 spectrum np.abs(fft(sig))[:len(sig)//2] freqs np.fft.fftfreq(len(sig), 1/fs)[:len(sig)//2] dominant_freq freqs[np.argmax(spectrum)] spectral_entropy -np.sum((spectrum/spectrum.sum()) * np.log(spectrum/spectrum.sum() 1e-8)) feats.extend([dominant_freq, spectral_entropy]) return np.array(feats) # 对每个窗口提取特征 feature_matrix np.array([extract_features(w) for w in X]) print(f特征矩阵形状: {feature_matrix.shape})每个窗口提取的特征维度是n_features * 9三个传感器就是27维。频域特征里主频和频谱熵对设备故障最敏感峰值因子对冲击类异常敏感。注意FFT之前要去均值否则直流分量会干扰主频判断。3.2 树模型与时序模型的组合策略这道题的目标通常是预测某个指标或做异常分类。我的经验是如果目标列和特征之间是非线性关系且特征维度高用XGBoost或LightGBM如果目标有明显时序依赖用LSTM或GRU最稳的方案是两者做 stacking。import lightgbm as lgb from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import mean_squared_error # 时间序列交叉验证不能随机打乱 tscv TimeSeriesSplit(n_splits5) params { objective: regression, metric: rmse, num_leaves: 31, learning_rate: 0.05, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 5, verbose: -1 } scores [] for train_idx, val_idx in tscv.split(feature_matrix): X_train, X_val feature_matrix[train_idx], feature_matrix[val_idx] y_train, y_val y[train_idx], y[val_idx] dtrain lgb.Dataset(X_train, y_train) dval lgb.Dataset(X_val, y_val, referencedtrain) model lgb.train(params, dtrain, num_boost_round500, valid_sets[dval], callbacks[lgb.early_stopping(50)]) pred model.predict(X_val, num_iterationmodel.best_iteration) scores.append(np.sqrt(mean_squared_error(y_val, pred))) print(fCV RMSE: {np.mean(scores):.4f} ± {np.std(scores):.4f})参数上num_leaves控制在31到63之间太大容易过拟合learning_rate用0.05配合early_stoppingfeature_fraction和bagging_fraction都设0.8增加鲁棒性。时间序列交叉验证必须用TimeSeriesSplit随机KFold会泄露未来信息这是很多人翻车的地方。3.3 模型融合加权平均还是Stacking单模型调好后融合能再涨一两个点。简单加权平均适合模型差异大的情况Stacking适合模型互补性强的情况。我一般先试加权不行再上Stacking。from sklearn.linear_model import Ridge from sklearn.ensemble import RandomForestRegressor # 基模型 lgb_model lgb.train(params, lgb.Dataset(feature_matrix, y), num_boost_round300) rf_model RandomForestRegressor(n_estimators200, max_depth10, random_state42) rf_model.fit(feature_matrix, y) # 加权平均权重通过验证集搜索 best_w, best_score 0, float(inf) for w in np.arange(0, 1.05, 0.05): blend w * lgb_model.predict(feature_matrix) (1-w) * rf_model.predict(feature_matrix) score np.sqrt(mean_squared_error(y, blend)) if score best_score: best_score, best_w score, w print(f最优权重: LGB{best_w:.2f}, RF{1-best_w:.2f}, RMSE{best_score:.4f})加权平均的权重搜索粒度0.05就够了再细容易过拟合验证集。Stacking的话用Ridge做元学习器把基模型的预测作为输入注意元学习器的训练数据要用交叉验证的折外预测否则还是会泄露。4. 论文写作与代码组织让评委一眼看到你的工作量建模竞赛的论文不是技术文档评委看的是逻辑清晰、图表规范、结论有支撑。这一章讲论文结构怎么搭、代码怎么组织才能既复现又好看。4.1 论文五段式结构与图表规范我带的队伍论文统一用这个结构问题重述与分析、数据预处理、模型建立与求解、结果分析与检验、模型评价与推广。每部分的比例大概是1:2:3:2:1模型部分是重头。图表规范上所有图必须带标题、坐标轴标签、单位。折线图用不同线型区分不要只靠颜色。表格用三线表参数表要标注取值范围和选择理由。我一般要求论文里至少包含数据缺失分布图、异常值检测图、特征重要性排序图、预测结果对比图、误差分布图。这五张图能把工作量直观展示出来。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False fig, axes plt.subplots(2, 2, figsize(12, 10)) # 缺失分布 df.isna().mean().plot(kindbar, axaxes[0,0], title各列缺失率) # 异常值 axes[0,1].plot(df.index, df[vibration], label原始) axes[0,1].scatter(high_conf_idx, df.loc[high_conf_idx, vibration], cred, label高置信异常, s10) axes[0,1].legend(); axes[0,1].set_title(振动异常检测) # 特征重要性 lgb.plot_importance(model, axaxes[1,0], max_num_features15) # 预测对比 axes[1,1].plot(y_val[:200], label真实) axes[1,1].plot(pred[:200], label预测) axes[1,1].legend(); axes[1,1].set_title(预测对比) plt.tight_layout() plt.savefig(paper_figures.png, dpi300)出图用300dpi字体统一SimHei负号要设置axes.unicode_minusFalse否则显示方块。特征重要性图用LightGBM内置的plot_importance比手动画省事。4.2 代码目录结构与复现说明代码目录我一般这样组织data/放原始数据src/放脚本output/放结果figures/放图。主脚本按01_preprocess、02_features、03_train、04_evaluate编号保证执行顺序清晰。project/ ├── data/ │ ├── raw/ │ └── processed/ ├── src/ │ ├── 01_preprocess.py │ ├── 02_features.py │ ├── 03_train.py │ └── 04_evaluate.py ├── output/ │ ├── models/ │ └── results/ ├── figures/ └── README.mdREADME里写清楚运行环境、依赖安装、执行顺序。依赖用requirements.txt固定版本避免评委环境不一致跑不通。我一般会加一个run_all.sh一键执行但注意路径要用相对路径别写死绝对路径。5. 避坑指南矿山数据处理赛题里最容易翻车的五个地方这一章是我带队和看别人论文总结的血泪经验每条都按现象、原因、解决来写。坑一用随机KFold做交叉验证。现象是验证集RMSE很低但测试集一塌糊涂。原因是时序数据有自相关性随机划分会让未来信息泄露到训练集。解决是改用TimeSeriesSplit并且特征工程里的滑动窗口不能跨折计算。坑二缺失值插值后不做标记。现象是模型在插值段预测偏差大。原因是插值数据是编的模型无法区分真实值和插值。解决是加一列缺失标记插值位置标1其余标0作为特征输入。坑三异常值直接删除。现象是数据量骤减模型欠拟合。原因是矿山数据的异常往往是真实工况删了就丢信息。解决是高置信异常置NaN走插值疑似异常保留并打标记。坑四特征不做归一化就喂给树模型。现象是树模型对某些特征不敏感。原因是树模型虽然对量纲不敏感但特征重要性会被量纲大的特征主导。解决是统一做标准化或者用基于排序的特征重要性。坑五论文里只放最终结果不放中间过程。现象是评委觉得工作量不够。原因是只展示了模型精度没展示数据清洗和特征工程的过程。解决是把缺失分布、异常检测、特征重要性这些中间图都放进论文让评委看到完整链路。6. 从赛题到落地矿山数据处理方案的复用与进阶这道题做完方案本身可以复用到很多工业时序场景。我一般会把预处理和特征工程部分封装成模块换个数据集就能跑。进阶方向有三个一是引入自动化特征工程工具比如tsfresh能自动提取几百个时序特征再筛选二是用深度学习模型做端到端比如TCN或Transformer省去手工特征但需要更多数据三是做在线推理把训练好的模型部署成API实时接收传感器数据做预测。验证方案是否靠谱我习惯做两件事一是用不同时间段的数据做回测看模型在时间上的稳定性二是做特征消融实验逐个去掉特征看性能下降多少下降多的就是核心特征。这两个动作能让论文的结论更有说服力。# 特征消融示例 base_score np.sqrt(mean_squared_error(y_val, model.predict(X_val))) importance model.feature_importance(importance_typegain) sorted_idx np.argsort(importance)[::-1] for i in sorted_idx[:10]: X_ablated X_val.copy() X_ablated[:, i] 0 ablated_score np.sqrt(mean_squared_error(y_val, model.predict(X_ablated))) print(f去掉特征{i}: RMSE上升{ablated_score - base_score:.4f})消融实验里RMSE上升最多的特征就是最关键的。这个结果可以直接写进论文的模型分析部分比单纯列特征重要性更有说服力。最后说个我自己的习惯每次做完题我会把踩过的坑和对应的解决代码整理成一个checklist下次遇到类似赛题先过一遍。这个习惯让我从第一次参赛的翻车到现在能稳定出活。矿山数据处理这道题难点不在模型多复杂而在数据链路每一步都别偷懒。希望帮到你。本文还有配套的精品资源点击获取