
简介数学建模比赛常用代码Python版是一份面向数模参赛者的实用代码合集系统覆盖从基础语法、数值计算到机器学习与可视化的常用建模工具适合希望快速搭建算法方案的读者。资源共71个文件压缩包约3.42MB以txt算法说明、docx模型文档、py可运行脚本为主另有dat数据文件与rar补充包便于按类别查阅。内容包括一维/二维插值、线性与非线性规划、整数与二次规划、动态规划、智能优化算法、TOPSIS、层次分析、灰色预测、主成分分析以及支持向量机、随机森林、决策树、BP/卷积神经网络等高频赛题模型基本覆盖数模竞赛常见题型。目前已有1976人浏览学习说明其在实际备赛中具有较好参考价值。配套代码与文档可帮助理解算法原理并迁移应用到具体赛题节省编码和调参时间。1. 数学建模比赛的代码不是越多越好三天赛程真正能跑的代码其实就那么几段。见过太多队伍第一天花在找代码上第二天发现下载的东西根本跑不通最后一天熬夜补论文。这份数学建模比赛常用代码Python版不是算法大全而是把数据读入、缺失值处理、异常值检测、模型训练、交叉验证、可视化出图这些高频环节封装成可以直接调用的脚本。它的价值在于赛前花两小时过一遍接口比赛时改数据路径和几个参数就能出结果。想清楚这点再下载它解决的是“有代码可用”和“代码能复现”的问题不是帮你自动得奖。适合参加过比赛但写过一堆重复代码的人也适合第一次参赛想少走弯路的新手。2. 先看懂这份代码包的结构文件职责与调用约定拿到压缩包先别急着运行第一步是搞清楚每个文件是干什么的。这份代码包按功能拆模块不是按算法拆文件。原因是数模比赛换题很频繁上午还在做分类下午可能就换成了优化模型按功能拆目录能让大部分代码跨题复用改动量最小。2.1 目录结构与各文件职责解压后典型的目录结构如下math_modeling_code/ ├── config.yaml # 全局配置文件 ├── data/ # 放比赛给的数据 │ ├── raw/ # 原始数据 │ └── processed/ # 清洗后的数据 ├── src/ │ ├── load_data.py # 数据读取模块 │ ├── preprocess.py # 清洗、缺失值、异常值 │ ├── feature_engineering.py # 特征构造 │ ├── models.py # 分类/回归/聚类模型封装 │ ├── evaluate.py # 交叉验证与评估 │ └── visualize.py # 可视化出图 ├── notebooks/ # 探索性分析的notebook └── submissions/ # 最终提交的结果文件各文件的核心职责可以这样理解文件职责对应比赛环节config.yaml统一管理路径和全局参数赛前配置load_data.py读入Excel/CSV处理编码和sheet数据准备preprocess.py缺失值、异常值、归一化数据清洗feature_engineering.py构造新特征、时间戳拆解特征工程models.py封装sklearn常见模型建模evaluate.py交叉验证、网格搜索模型选择visualize.py出图并保存到指定目录论文配图这种结构的好处是赛场上如果发现数据格式不对只需要改load_data.py不需要碰模型代码如果发现模型效果差只需要改models.py和evaluate.py不需要重写数据清洗逻辑。模块边界清晰排查问题的时候不会上下游一起翻车。2.2 数据读取模块的接口约定load_data.py是第一个要看的文件。它把所有读取逻辑收敛到两个函数里避免每道题都重新写一遍pd.read_csv还要反复处理编码报错。# src/load_data.py import pandas as pd from pathlib import Path def load_csv(file_path, sep,, encodingutf-8): 读取CSV自动处理常见编码错误。 file_path: 文件路径 sep: 分隔符默认逗号 encoding: 默认utf-8失败时尝试gbk try: return pd.read_csv(file_path, sepsep, encodingencoding) except UnicodeDecodeError: return pd.read_csv(file_path, sepsep, encodinggbk)逻辑说明优先按utf-8读取遇到解码错误自动转gbk这是国产赛题Excel另存为CSV时最常见的坑。参数上sep要按实际数据调整有些题目给的是分号或制表符分隔不改这个参数读进来的就是一整列。def load_excel(file_path, sheet_name0): 读取Excelsheet_name默认第一个sheet。 返回DataFrame统一把空单元格转为NaN。 df pd.read_excel(file_path, sheet_namesheet_name) return df这里有个约定所有读取接口返回的都是DataFrame且空值统一变成NaN。比赛后期如果发现哪一步计算出现奇怪的数值第一步先检查是不是源头读取时把空字符串当成了有效数据。2.3 全局参数与特定参数怎么分离config.yaml的作用是让队友之间不互相踩配置。全局参数放数据路径、随机种子、输出目录模型相关参数不放在这里而是放在models.py的模型封装函数里。# config.yaml data: raw_dir: data/raw processed_dir: data/processed target_col: y # 目标列名按题目修改 id_col: id # 主键列名预测时用于对齐 experiment: random_seed: 42 # 固定随机种子保证结果可复现 cv_folds: 5 # 交叉验证折数 test_size: 0.2 # 留出验证集比例固定随机种子这个参数经常被忽略。比赛里同一套代码跑两次结果不一样八成是没设random_state。配置文件里加一行所有用到随机的函数都读这个值能省掉很多不必要的争论。2.4 为什么用Python而不是MATLAB在这个代码包的语境下选Python主要是三个现实原因第一数据清洗和缺失值处理用pandas比MATLAB直接得多第二机器学习模型这块sklearn的成熟度在比赛场景下够用随机森林、梯度提升树都是几行代码的事第三可视化出图后可以无缝接入论文。MATLAB在规划求解、微分方程数值解上仍然有优势但这份代码包定位是数据类赛题的高频流程Python的性价比更高。如果赛题涉及大规模线性规划或偏微分方程建议再准备专门的MATLAB工具箱脚本不是说这份包里没写就代表Python不能做而是场景不匹配硬写只会浪费时间。3. 数据预处理脚本的三板斧读入、清洗、变换数据预处理是数模比赛里最耗时的一环。评审看不到你清洗数据花了多少工夫但模型效果差、图表异常根子基本都在预处理。手里有一套稳定的预处理脚本能让你把更多时间留给特征工程和论文撰写。3.1 读取CSV的编码与分隔符问题比赛提供的CSV文件来源复杂有些是从Excel另存的有些是爬虫抓的编码和分隔符不统一是常态。# src/preprocess.py 配套的读取示例 import pandas as pd file_path data/raw/train.csv # 读取时先看前几行确认解析正确 try: df pd.read_csv(file_path, encodingutf-8) except UnicodeDecodeError: df pd.read_csv(file_path, encodinggbk) # 检查列数是否是预期值 expected_cols 15 if df.shape[1] ! expected_cols: print(f列数异常期望{expected_cols}列实际{df.shape[1]}列)逻辑说明第一次读取成功后不要直接进入建模先打印shape和head()看列数是否正常。常见问题是分隔符是分号或制表符读进来所有列挤成一列这时候手动指定sep参数比写自动推断靠谱。自动推断看起来方便但遇到特殊字符容易静默出错。3.2 缺失值处理的分层策略缺失值处理没有万能公式要看数据规模和缺失比例。这套代码里给出三个处理函数按场景选用# src/preprocess.py import numpy as np def drop_missing(df, threshold0.4): 删除缺失比例超过threshold的列。 threshold: 缺失率阈值默认40%以上直接删列 missing_ratio df.isnull().mean() drop_cols missing_ratio[missing_ratio threshold].index return df.drop(columnsdrop_cols) def fill_missing_by_value(df, fill_dict): 按指定值填充fill_dict格式为 {列名: 填充值}。 数值型用中位数/均值类别型用众数。 return df.fillna(fill_dict) def fill_missing_by_interpolate(df, methodlinear): 对时间序列类数据使用插值填充。 method: 插值方法linear或quadratic return df.interpolate(methodmethod)参数说明缺失比例阈值的设置要看样本量。样本量大的时候删除一列影响不大样本量小的时候比如只有几百行宁可保留缺失列用填充策略。中位数比均值抗异常值干扰类别特征用众数填充这是默认习惯。插值方法适合时间序列补值但前几个位置补不了需要另配向前填充。3.3 异常值检测与处理异常值检测有两个方向一是数据本身录入错误二是真实存在但会干扰模型拟合的极端值。比赛中一般不做剔除先标记和处理。# src/preprocess.py def detect_outliers_iqr(df, col, multiplier1.5): 基于IQR方法检测异常值。 multiplier: IQR倍数默认1.5为标准区间 Q1 df[col].quantile(0.25) Q3 df[col].quantile(0.75) IQR Q3 - Q1 lower Q1 - multiplier * IQR upper Q3 multiplier * IQR return df[(df[col] lower) | (df[col] upper)].index def detect_outliers_zscore(df, col, threshold3): 基于Z分数检测异常值。 threshold: Z分数阈值默认超过3倍标准差视为异常 z np.abs((df[col] - df[col].mean()) / df[col].std()) return df[z threshold].index逻辑说明IQR方法不依赖均值对偏态分布更稳健Z分数方法计算快但容易被极端值本身拉偏均值。比赛时两种方法都跑一遍把两份异常集合做交集命中交集的样本才处理能减少误删。处理方式一般是先看一下这些异常值是录入错误还是真实值不要一上来就删行。3.4 归一化与标准化怎么选归一化和标准化经常被混用实际上对距离类模型影响很大。代码包里分别封装了两个函数# src/preprocess.py from sklearn.preprocessing import StandardScaler, MinMaxScaler def scale_standard(df, cols): 标准化均值为0标准差为1。保留异常值信息。 cols: 需要缩放的列名列表 scaler StandardScaler() df[cols] scaler.fit_transform(df[cols]) return df, scaler def scale_minmax(df, cols, feature_range(0, 1)): 归一化缩放到feature_range范围默认0-1。 对有限范围的数据更友好但受异常值影响大。 scaler MinMaxScaler(feature_rangefeature_range) df[cols] scaler.fit_transform(df[cols]) return df, scaler注意fit_transform返回的是numpy数组直接赋值回DataFrame会丢列名代码里用df[cols]的方式保持索引对齐。实际使用中如果后续要用PCA或聚类这类基于距离的算法先做标准化收敛更快如果特征本身物理意义有明确上下界比如经纬度、百分比用MinMax归一化更合理。4. 模型脚本的选择逻辑分类、回归、聚类的调用姿势模型脚本这一章是代码包的核心但也是最容易踩坑的地方。很多队伍拿到数据就直接套随机森林根本不看任务类型和数据规模。这套代码把模型封装成统一的函数内部设置好默认参数同时保留改参入口。4.1 分类场景逻辑回归与随机森林逻辑回归是基线模型随机森林是主力模型。代码包里默认优先跑逻辑回归原因是可以快速验证数据预处理是否正确。# src/models.py from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier def train_logistic(X_train, y_train, C1.0, penaltyl2): C: 正则化强度的倒数越小正则越强 penalty: l1或l2l1可做特征选择 model LogisticRegression(CC, penaltypenalty, max_iter1000, random_state42) model.fit(X_train, y_train) return model def train_rf_classifier(X_train, y_train, n_estimators200, max_depthNone): n_estimators: 树的数量默认200 max_depth: 树的最大深度None表示不限制 model RandomForestClassifier( n_estimatorsn_estimators, max_depthmax_depth, min_samples_leaf2, random_state42, n_jobs-1 ) model.fit(X_train, y_train) return model参数说明逻辑回归里max_iter调到1000是因为sklearn默认100在特征多时可能不收敛比赛数据经常几百个特征跑出警告说明迭代次数不够。随机森林的min_samples_leaf2是为了防止叶子节点过细造成过拟合。n_jobs-1表示用全部CPU核心比赛时性能提升明显。4.2 回归场景线性回归与梯度提升树回归任务中线性回归作为基线梯度提升树作为主力。比赛评分通常看误差指标梯度提升树在非线性和特征交互场景下容错率更高。# src/models.py from sklearn.linear_model import LinearRegression from sklearn.ensemble import HistGradientBoostingRegressor def train_linear(X_train, y_train): model LinearRegression() model.fit(X_train, y_train) return model def train_gbr(X_train, y_train, max_iter200, learning_rate0.1): max_iter: 迭代轮数树的数量 learning_rate: 学习率越小越保守但需要更多迭代 model HistGradientBoostingRegressor( max_itermax_iter, learning_ratelearning_rate, random_state42 ) model.fit(X_train, y_train) return model逻辑说明HistGradientBoostingRegressor比普通GradientBoostingRegressor在数据量大时更快且对缺失值有内置处理比赛数据预处理没做好时这是个后悔药。learning_rate和max_iter是一对关系学习率设小就要增大迭代次数不然欠拟合。4.3 聚类场景KMeans与层次聚类聚类在数模赛题里通常是做数据探索的一部分比如客户分群、区域划分。KMeans方便但聚类数K要靠轮廓系数判断。# src/models.py from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score def kmeans_with_silhouette(X, k_range(2, 10), random_state42): 对一系列K值计算轮廓系数返回最佳K和对应的模型。 k_range: K值扫描范围 best_k None best_score -1 best_model None for k in range(k_range[0], k_range[1] 1): model KMeans(n_clustersk, random_staterandom_state, n_initauto) labels model.fit_predict(X) score silhouette_score(X, labels) if score best_score: best_score score best_k k best_model model return best_k, best_model, best_scoren_init参数在新版sklearn里默认改成auto老版本需要显式设置n_init10否则会有收敛警告。轮廓系数只对凸簇数据友好如果数据形状复杂要结合可视化判断别只看分数选K。4.4 交叉验证与网格搜索的固定套路网格搜索是比赛里调参的基本操作但很多人直接把整个参数网格丢进去结果跑了一夜没出结果。# src/evaluate.py from sklearn.model_selection import GridSearchCV def search_params(model, param_grid, X_train, y_train, cv5, n_jobs-1): param_grid: 参数网格字典 cv: 交叉验证折数默认5 n_jobs: 并行计算核心数-1表示全部 grid GridSearchCV( estimatormodel, param_gridparam_grid, cvcv, scoringf1 if model._estimator_type classifier else neg_mean_squared_error, n_jobsn_jobs, verbose1 ) grid.fit(X_train, y_train) return grid.best_params_, grid.best_score_参数说明scoring根据模型类型自动切换分类用F1回归用负均方误差这样统一接口不会传错参数。网格搜索前先评估参数组合数量超过20组就要考虑随机搜索或者缩小范围否则比赛时间不够用。GridSearchCV在数据量大时内存消耗很高建议先对训练集做一次subsample测试确认能跑通再放全量。5. 用这份代码踩过的坑现象、原因与解法代码跑不通不是最痛苦的最痛苦的是程序没报错、输出一片乱。这一章把高频翻车点按“现象、原因、解决”写清楚都是实际比赛里能救命的经验。5.1 读取数据出现乱码或列名错位现象pd.read_csv读入后打印df.head()发现列名是乱码或者第一行数据变成了列名所有列往左错了一位。原因UTF-8编码的CSV被Excel打开再另存后成了GBK编码或者表格里第一行本来就是数据没有表头。列名错位更常见于某些题目直接把变量名放在第二行而read_csv默认把第一行当表头。解决先打开原始文件确认结构再用load_csv函数读取。遇到没有表头的文件给read_csv加headerNone然后手动指定列名。我用过的最快方案是写一个临时脚本一次性打印文件前五行字节内容确认编码后再定参数。5.2 缺失值直接删除导致样本量骤减现象调用dropna()后训练集从一万行掉到两千行后面模型怎么训练都过拟合。原因数据里多个特征都带缺失只要任一行任意列有缺失就删行样本量直接被砍掉大半。这个操作对数据量小的赛题是灾难。解决改用列删除先删缺失率大于40%的特征列再对剩余特征逐列填充对于关键特征不要用均值硬填先分组后填充比如按时间分组的中位数填充效果比全局均值好很多。5.3 标准化时把训练集和测试集一起fit现象模型训练时验证集效果不错提交后离线测试分数明显偏低。原因先对全量数据做了标准化fit_transform再切分训练集和测试集导致测试集信息提前泄露到scaler的均值和方差里。竞赛不是不能用全量数据但做模型评估时必须保证每个fold的scaler只在训练fold上fit。解决用ColumnTransformer或Pipeline把标准化和模型放进同一个Pipeline交叉验证时每个fold自动重新fit。在比赛里想快速验证就先切分再fit# 正确的顺序先切分再fit scaler from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)逻辑说明fit_transform方法在测试集上不能用只能用fit后transform确保测试集不参与缩放参数的估计。这个坑报错概率很低因为代码能跑只是结果偏保守或偏乐观。5.4 网格搜索时间爆炸现象GridSearchCV跑了一小时还没结束队友已经在催论文截图了。原因param_grid里参数组数太多交叉验证又设了10折数据量几万行计算量成倍放大。比赛时间有限这种做法属于方案设计失误。解决网格搜索之前先算参数组合数量超过20组就改用RandomizedSearchCV或手动逐个试。配合n_jobs-1并行同时把cv从10降到5。还有一个习惯是先在2万行的子集上跑一版确认参数范围合理再放全量。5.5 特征量纲没统一导致聚类结果全偏现象聚类后的中心点坐标看着没问题但画到图上一团糟明显有几个特征主导了距离计算。原因KMeans用的是欧氏距离量纲大的特征数值天然占优势。比如收入特征取值范围几万年龄特征只有几十聚类结果基本只看收入。解决聚类前必须做标准化或归一化这个不是可选项是必须项。解决后重新跑轮廓系数分数会明显提升。比赛里如果聚完之后还想继续做分析记得把聚类中心反标准化回原尺度再解释。6. 赛前最后一小时把这套代码跑成一条流水线比赛最后一天最怕临时发现某一步报错。我的做法是把所有环节串成一个pipeline脚本输入原始数据路径输出预测结果文件和模型指标。# 赛前快速验证data - result 一条命令跑完 import pandas as pd from src.load_data import load_csv from src.preprocess import drop_missing, fill_missing_by_value, scale_standard from src.models import train_rf_classifier from sklearn.model_selection import train_test_split from sklearn.metrics import f1_score # 1. 读数据 df load_csv(data/raw/train.csv) # 2. 清洗删高缺失列填充剩余缺失 df drop_missing(df, threshold0.4) df df.fillna(df.median()) # 3. 特征与标签分离 X df.drop(columns[id, y]) y df[y] # 4. 切分并标准化 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) X_train, scaler scale_standard(X_train) X_test[X_train.columns] scaler.transform(X_test[X_train.columns]) # 5. 训练并评估 model train_rf_classifier(X_train, y_train, n_estimators200) pred model.predict(X_test) print(fF1: {f1_score(y_test, pred):.4f})这段脚本每个比赛开始前跑一遍确认从读入到出指标全链路无报错。比赛过程中如果改了配置或模型再跑一遍这个脚本做回归验证能及时发现新代码引入的bug。文档开赛前花一天时间把代码包的每个模块跑一次确保数据清洗、模型训练、出图三块都能单独工作。比赛时问自己三个问题缺失值处理了吗特征和标签对齐了吗预测结果有没有统一格式三个都确认再提交。核心的代码逻辑其实在比赛第一天就定了后面两天更多是把同一个流程迭代熟练。从那以后我每次比赛前都强制走一遍这个流程先跑通pipeline再讨论模型调优。希望这份代码包能帮你把踩坑的成本降下来把时间花在真正拉开差距的地方。本文还有配套的精品资源点击获取