2024年MathorCup C题:从数据预处理到排班优化的全链路建模实战
1. 数据预处理从脏数据到干净样本的关键步骤数据预处理是数学建模竞赛中最容易被忽视却又至关重要的环节。在实际项目中我见过太多团队因为跳过这一步直接建模而导致预测结果偏离实际。以2024年MathorCup C题为例附件1中的货量数据存在明显的极端值——有些记录高达10万有些则低至个位数。这种数据如果不经处理直接喂给模型就像让厨师用发霉的食材做菜结果可想而知。处理异常值有几种经典方法。对于近似正态分布的数据我推荐使用3σ原则计算数据的均值μ和标准差σ将超出[μ-3σ, μ3σ]范围的值视为异常。但实际业务数据往往呈偏态分布这时箱线图法更可靠。具体操作时先计算第一四分位数Q1、第三四分位数Q3和四分位距IQR任何低于Q1-1.5IQR或高于Q31.5IQR的数据点都需要特别关注。import pandas as pd import numpy as np # 加载数据示例 data pd.read_csv(attachment1.csv, encodingGBK) # 箱线图法检测异常值 Q1 data[货量].quantile(0.25) Q3 data[货量].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR outliers data[(data[货量] lower_bound) | (data[货量] upper_bound)]但异常值处理不是简单的删除。比如我们发现某天货量突增到11000件经查证是双十一活动导致。这类业务合理的异常应该保留但可能需要单独建模或添加活动标记作为特征。我常用的处理流程是先自动化检测异常→人工复核业务合理性→对确认的异常值进行修正或标记。2. 货量预测模型时间序列与机器学习的融合实战2.1 日货量预测的模型选型面对57个分拣中心未来30天的货量预测需求我们需要区分日预测和小时预测两种场景。对于日货量预测传统时间序列模型往往比通用机器学习模型更占优势。我的项目经验表明SARIMA模型季节性自回归综合移动平均模型在物流货量预测中表现稳定尤其适合具有明显周周期性的数据。SARIMA的核心参数包括非季节性部分(p,d,q)分别代表自回归阶数、差分次数和移动平均阶数季节性部分(P,D,Q,m)其中m为周期长度如周数据m7常用参数组合(1,1,1)×(1,1,1,7)是个不错的起点from statsmodels.tsa.statespace.sarimax import SARIMAX # 以单个分拣中心为例 model SARIMAX(daily_data, order(1, 1, 1), seasonal_order(1, 1, 1, 7)) results model.fit() forecast results.get_forecast(steps30)但SARIMA对长期预测如30天可能存在误差累积问题。这时可以结合Prophet模型——Facebook开源的预测工具能自动处理节假日效应且对缺失值不敏感。去年国赛中有队伍使用Prophet取得优异成绩但要注意其需要足够的历史数据才能发挥优势。2.2 小时级预测的两种思路小时货量预测的挑战在于数据粒度更细、波动更大。我实践过两种有效方案方案一整体时序建模将24小时视为一个完整周期用所有历史小时数据约3万条记录直接训练模型。这种方法的优势是数据量大、能捕捉微观波动但需要足够强的算力和精细的参数调优。方案二分时段独立预测把每天同一小时的数据单独提取如只取每天上午10点的数据形成30天的序列单独预测。这样做减少了数据量但需要执行24次预测且可能丢失小时之间的关联信息。我的实测对比显示对于业务高峰时段如上午10-12点方案二预测更准确而平峰时段方案一表现更好。因此在实际比赛中可以采用混合策略对关键时段使用分时预测其余时段用整体建模。3. 运输线路变化下的预测调整当问题引入运输线路变化附件3→附件4时预测模型需要升级为多变量时序模型。这时除了时间维度还要考虑空间维度的影响。我的团队采用的特征工程方案包括线路货量变动特征新线路标记0/1历史货量变化率ΔQ/Q相邻线路货量均值分拣中心网络特征入度/出度作为枢纽的重要性平均运输距离服务区域经济指标# 特征构造示例 merged_data[是否新线路] (merged_data[历史货量] 0).astype(int) merged_data[货量变化率] merged_data[当前货量] / merged_data[历史货量].replace(0, 1)在模型选择上XGBoost这类树模型对特征工程非常友好。以下是关键参数配置建议learning_rate: 0.01-0.1小学习率配合更多树max_depth: 5-8防止过拟合subsample: 0.8行采样colsample_bytree: 0.8列采样import xgboost as xgb model xgb.XGBRegressor( n_estimators1000, learning_rate0.05, max_depth6, subsample0.8, colsample_bytree0.8, random_state42 ) model.fit(X_train, y_train)对于深度学习的尝试我建议使用LSTMAttention结构处理时序部分再拼接静态特征。但要注意神经网络需要更多数据且训练时间长在比赛时间有限的情况下可能不是最优选择。4. 排班优化从预测到决策的最后一公里4.1 基础排班模型构建有了货量预测后排班优化就转化为典型的整数规划问题。以问题三为例我们需要在满足货量需求的前提下最小化总人天数。这需要明确定义决策变量x[i][j]: 第i天第j班次的正式工人数y[i][j]: 第i天第j班次的临时工人数目标函数 Minimize ΣΣ(x[i][j] y[i][j])约束条件产能约束Px[i][j] Ty[i][j] ≥ C[i][j]/6 P25为正式工人效T20为临时工人效正式工上限x[i][j] ≤ 60非负约束x[i][j] ≥ 0, y[i][j] ≥ 0单班次约束Σx[i][j] ≤ 1每人每天最多一个班次使用PuLP库可以方便地建模求解from pulp import * prob LpProblem(Shift_Scheduling, LpMinimize) # 定义变量 x LpVariable.dicts(正式工, (days, shifts), lowBound0, catInteger) y LpVariable.dicts(临时工, (days, shifts), lowBound0, catInteger) # 目标函数 prob lpSum([x[d][s] y[d][s] for d in days for s in shifts]) # 添加约束 for d in days: for s in shifts: prob 25*x[d][s] 20*y[d][s] predicted_workload[d][s]/6 prob x[d][s] 60 prob lpSum([x[d][s] for s in shifts]) 1 prob.solve()4.2 复杂约束下的进阶优化问题四增加了更多现实约束正式工出勤率≤85%连续出勤≤7天出勤率均衡性要求这时需要引入二元决策变量x[i][j][k]: 第k名员工在第i天第j班次是否出勤0/1新的约束条件包括# 出勤率约束 for k in employees: prob lpSum([x[d][s][k] for d in days for s in shifts]) 25.5 # 85% of 30 # 连续出勤约束 for k in employees: for d in range(len(days)-7): prob lpSum([x[di][s][k] for i in range(8) for s in shifts]) 7 # 均衡性通过辅助变量实现 avg_attendance 25.5 / len(employees) for k in employees: prob attendance[k] avg_attendance - delta prob attendance[k] avg_attendance delta在实际编码中这类大规模整数规划问题可能需要列生成法等高级优化技术分解为多个子问题迭代求解使用CPLEX/Gurobi等商业求解器如果比赛允许5. 工程实践中的常见陷阱与解决方案5.1 数据泄露问题在时间序列预测中最常见的错误是未来信息泄露。比如在特征工程时如果使用全局统计量如整个时间段的均值进行标准化就会导致模型偷看未来数据。正确的做法是采用滚动窗口统计# 错误的全局标准化 scaler StandardScaler() scaler.fit(all_data) # 泄露未来信息 # 正确的滚动标准化 window_size 30 for i in range(window_size, len(data)): window data[i-window_size:i] current data.iloc[i] scaled_value (current - window.mean()) / window.std()5.2 预测与优化的衔接很多团队在预测阶段表现优异却在优化阶段简单地将预测值直接代入导致排班方案不符合实际。我的经验是保留预测的不确定性提供分位数预测而非单点估计采用鲁棒优化考虑最坏情况下的资源需求增加缓冲人员根据预测误差的统计分布设置安全边际5.3 结果可视化技巧评委通常需要快速理解大量结果数据好的可视化能事半功倍。我推荐热力图矩阵展示57个分拣中心的预测货量变化import seaborn as sns pivot_table df.pivot(日期, 分拣中心, 货量) sns.heatmap(pivot_table, cmapYlGnBu)甘特图直观显示员工排班计划动态预测区间图展示预测值及置信区间在最终论文中应该包含完整的代码实现关键步骤但要注意删除调试代码和冗余输出添加必要的注释保持代码风格一致提供简明的README说明运行环境依赖