数学建模竞赛实战:Python实现生态保护评价与TOPSIS模型

📅 2026/8/27 10:15:10
数学建模竞赛实战:Python实现生态保护评价与TOPSIS模型
1. 项目概述从竞赛题目到完整解决方案的构建之路拿到“2021年亚太杯APMCM数学建模大赛C题生态保护的建设及其对环境影响的评价求解全过程文档及程序”这个标题很多参加过数模竞赛的朋友可能会心一笑。这不仅仅是一个简单的项目归档它背后浓缩的是一支队伍在96小时高压下的完整思考、技术攻关与成果呈现。亚太杯APMCM作为亚太地区颇具影响力的数学建模赛事其C题通常聚焦于具有现实意义的交叉学科问题2021年的这道生态保护评价题正是典型代表。它要求参赛者不仅要建立数学模型来量化生态保护措施的效果还要评估其对环境的综合影响最终形成一套包含模型、算法、求解、可视化及分析的报告与程序。对于后来者无论是学习数模方法还是准备类似竞赛这样一个“全过程”文档和程序的价值是巨大的。它像一份详尽的“战地笔记”记录了从题目解析、数据预处理、模型构建、编程求解到论文撰写的每一个关键决策和踩过的坑。本文将以此项目为蓝本深度拆解其核心工作流并分享如何利用Python生态中的利器如pandas、numpy、matplotlib、openpyxl高效地完成从数据到结论的全过程。无论你是数模新手想了解标准流程还是有一定基础希望提升工程化实现能力这篇文章都将提供可直接复现的实战经验。2. 赛题核心解析与解题思路设计2.1 题目内涵与核心需求拆解2021年APMCM C题通常围绕“生态保护建设”与“环境影响评价”两大主题展开。这类题目本质上是一个综合评价与预测问题。我们需要构建数学模型来达成以下几个核心目标生态保护措施的效果量化题目可能会给出某种或某几种生态保护方案例如退耕还林还草、建立自然保护区、实施生态补水等。我们需要建立模型量化这些措施在特定指标如植被覆盖率、物种丰富度、水土保持能力上的提升效果。环境影响的综合评价生态保护措施在带来正面效益的同时也可能产生间接的负面影响或成本如初期资金投入、对局部小气候的扰动、对当地社区经济的短期影响等。模型需要能够综合正负两方面的影响给出一个全局性的评价。多方案比较与决策支持最终往往需要对不同保护方案进行排序或优选为决策者提供依据。这就要求模型具备可比性和区分度。在实际解题时我们首先需要将模糊的题目描述转化为具体的数学问题。例如“生态保护的建设”可能对应着一组随时间或空间变化的控制变量如植树面积、资金分配比例“对环境影响的评价”则对应着一组需要优化的目标函数或评价指标体系。2.2 解题技术路线规划面对这样一个复杂问题单靠一个模型是难以应对的。成熟的数模解决方案通常采用分阶段、多模型融合的技术路线。以下是我们团队当时设计的核心思路基础分析阶段问题一通常要求对现状进行评价或对单一措施进行模拟。我们采用系统动力学System Dynamics, SD模型或灰色预测模型来刻画生态系统中各要素如植被、水土、生物之间的相互作用与动态变化。SD模型擅长处理带有反馈、非线性的系统问题非常适合生态领域。综合评价阶段问题二这是题目的核心。我们构建了一个AHP-熵权法组合赋权的TOPSIS评价模型。为什么用组合赋权主观赋权法如AHP能体现专家对“生物多样性”、“水土保持”等不同指标重要性的经验判断客观赋权法如熵权法能根据各方案指标数据的离散程度自动确定权重避免主观偏见。将两者结合评价结果更为科学、稳健。为什么用TOPSIS优劣解距离法TOPSIS原理直观计算相对简单能清晰地对各方案进行排序找出与理想最优解最近、与理想最劣解最远的方案非常适合多方案选优问题。预测优化阶段问题三在评价基础上可能需要对未来情景进行预测或对措施参数进行优化。我们引入了BP神经网络进行非线性预测并尝试使用遗传算法GA对保护措施的投入配比进行优化以寻求在预算约束下的最佳环境效益。注意模型选择不是一成不变的。关键在于模型必须紧密贴合题目要求。例如如果题目数据量小、信息不全灰色预测和AHP就更合适如果数据量大、关系复杂神经网络和系统动力学的优势就更明显。在论文中必须清晰阐述你选择某个模型的理由。3. 数据驱动下的模型实现Python工具链实战数学建模竞赛“建”是思想“模”是骨架而“数据”和“程序”则是血肉。Python因其强大的科学计算和数据处理库已成为数模事实上的标准工具。下面以我们的解题流程为例详解如何用Python工具链将模型落地。3.1 数据预处理pandas与openpyxl的黄金组合赛题数据通常以Excel表格形式提供。高效、无误地读取和处理数据是第一步也是避免后续一切错误的基石。import pandas as pd from openpyxl import load_workbook # 1. 使用pandas读取数据这是最主流、高效的方式 data_df pd.read_excel(ecological_data.xlsx, sheet_nameSheet1) print(数据概览) print(data_df.head()) print(data_df.info()) # 查看数据类型和缺失值 # 2. 复杂Excel操作当需要读取特定区域、或Excel文件有特殊格式如合并单元格时openpyxl更灵活 wb load_workbook(ecological_data.xlsx, data_onlyTrue) # data_only确保读取计算后的值 ws wb[复杂数据表] # 例如读取一个非标准起始区域的数据 data_list [] for row in ws.iter_rows(min_row5, max_col8, max_row50, values_onlyTrue): # 这里可以加入数据清洗逻辑如处理None值 cleaned_row [0 if cell is None else cell for cell in row] data_list.append(cleaned_row) # 将openpyxl读取的数据转为DataFrame custom_df pd.DataFrame(data_list[1:], columnsdata_list[0]) # 假设第一行是标题 # 3. 数据清洗与准备 # 处理缺失值对于连续变量用列均值填充对于类别变量用众数填充 data_df.fillna(data_df.mean(), inplaceTrue) # 填充数值列 # 数据标准化为后续综合评价模型准备 from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler() normalized_data scaler.fit_transform(data_df.select_dtypes(include[number])) normalized_df pd.DataFrame(normalized_data, columnsdata_df.select_dtypes(include[number]).columns)实操心得pandas.read_excel是首选它能将数据快速加载到DataFrame中方便进行向量化操作。当遇到不规则表格如标题在多行、中间有空白行时openpyxl的单元格级控制能力无可替代。可以先用它把数据“抠”出来再交给pandas处理。务必在数据读取后立即进行info()和describe()检查了解数据规模、类型和分布及早发现异常值或缺失值。3.2 模型核心算法实现numpy与sklearn的威力模型的计算核心依赖于高效的数值运算。numpy和sklearn提供了坚实的基础。以TOPSIS模型实现为例import numpy as np def topsis(data, weightNone, impactNone): 实现TOPSIS评价算法。 :param data: 二维numpy数组行代表方案列代表指标。 :param weight: 一维数组各指标的权重。如果为None则等权。 :param impact: 一维数组每个指标的方向 表示效益型- 表示成本型。 :return: 各方案的综合得分及排序。 # 1. 数据标准化向量归一化法与熵权法常用的一致 norm np.sqrt((data ** 2).sum(axis0)) normalized data / norm # 2. 赋权 if weight is None: weight np.ones(data.shape[1]) / data.shape[1] weighted normalized * weight # 3. 确定理想解与负理想解 # 默认所有指标为效益型越大越好 if impact is None: impact [] * data.shape[1] ideal_best [] ideal_worst [] for i, col in enumerate(weighted.T): # 遍历每一列指标 if impact[i] : ideal_best.append(col.max()) ideal_worst.append(col.min()) else: # 成本型指标 ideal_best.append(col.min()) ideal_worst.append(col.max()) ideal_best np.array(ideal_best) ideal_worst np.array(ideal_worst) # 4. 计算各方案到理想解的距离 dist_best np.sqrt(((weighted - ideal_best) ** 2).sum(axis1)) dist_worst np.sqrt(((weighted - ideal_worst) ** 2).sum(axis1)) # 5. 计算相对贴近度 score dist_worst / (dist_best dist_worst 1e-10) # 加一个小数防止除零 # 6. 排序 rank score.argsort()[::-1] 1 # 得分从高到低排序并转为1起始的排名 return score, rank # 假设我们有一个3个方案、4个指标的评价矩阵 evaluation_matrix np.array([ [80, 90, 75, 60], # 方案A [70, 85, 90, 80], # 方案B [90, 70, 80, 70], # 方案C ]) weights np.array([0.3, 0.3, 0.2, 0.2]) # AHP-熵权法计算得到的组合权重 impacts [, , , -] # 假设前三个指标越大越好第四个指标如成本越小越好 scores, ranks topsis(evaluation_matrix, weights, impacts) print(各方案TOPSIS得分:, scores) print(方案排名1为最优:, ranks)AHP层次分析法实现关键点 AHP的核心是构造判断矩阵并计算权重、进行一致性检验。这部分需要自己实现逻辑numpy的线性代数模块np.linalg.eig用于求特征值和特征向量是关键。def ahp_weight(judgment_matrix): 计算AHP判断矩阵的权重向量并进行一致性检验。 # 计算特征值和特征向量 eigenvalues, eigenvectors np.linalg.eig(judgment_matrix) max_eigval np.max(eigenvalues.real) max_index np.argmax(eigenvalues.real) eig_vec eigenvectors[:, max_index].real # 归一化得到权重 weights eig_vec / eig_vec.sum() # 一致性检验 n judgment_matrix.shape[0] CI (max_eigval - n) / (n - 1) RI [0, 0, 0.58, 0.90, 1.12, 1.24, 1.32, 1.41, 1.45, 1.49] # 平均随机一致性指标 CR CI / RI[n-1] if n-1 len(RI) else 0 if CR 0.1: print(f一致性检验通过CR{CR:.4f}) else: print(f警告判断矩阵一致性不佳CR{CR:.4f} 0.1请调整判断矩阵。) return weights, CR3.3 结果可视化matplotlib与seaborn让结论一目了然“一图胜千言”在数模论文中清晰、专业的图表至关重要。matplotlib是基础seaborn能让统计图表更美观。import matplotlib.pyplot as plt import seaborn as sns plt.rcParams[font.sans-serif] [SimHei] # 用来正常显示中文标签 plt.rcParams[axes.unicode_minus] False # 用来正常显示负号 # 示例1各生态保护方案综合评价得分柱状图 schemes [方案A退耕还林, 方案B湿地修复, 方案C综合治理] topsis_scores [0.72, 0.85, 0.63] # 假设的TOPSIS得分 fig, ax plt.subplots(figsize(10, 6)) bars ax.bar(schemes, topsis_scores, color[#2E86AB, #A23B72, #F18F01]) ax.set_ylabel(TOPSIS相对贴近度, fontsize12) ax.set_title(不同生态保护方案综合评价结果, fontsize14, pad20) # 在柱子上方添加数值标签 for bar, score in zip(bars, topsis_scores): height bar.get_height() ax.text(bar.get_x() bar.get_width()/2., height 0.01, f{score:.3f}, hacenter, vabottom) ax.set_ylim(0, 1.0) plt.tight_layout() plt.savefig(scheme_comparison.png, dpi300) # 保存高清图用于论文 plt.show() # 示例2使用seaborn绘制指标相关性热力图用于分析指标间关系 import pandas as pd # 假设indicator_df是一个包含多个指标数据的DataFrame indicator_df pd.DataFrame({ 植被覆盖率: [0.8, 0.7, 0.9, 0.6], 物种丰富度: [85, 78, 92, 70], 土壤固碳量: [120, 110, 130, 100], 建设成本: [500, 600, 450, 700] }) plt.figure(figsize(8, 6)) sns.heatmap(indicator_df.corr(), annotTrue, cmapcoolwarm, center0, squareTrue, linewidths.5, cbar_kws{shrink: .8}) plt.title(生态评价指标相关性热力图, fontsize14) plt.tight_layout() plt.savefig(correlation_heatmap.png, dpi300) plt.show()图表制作避坑指南字体问题务必在代码开头设置中文字体否则图表中的中文会显示为方框。保存为图片时也要确保嵌入字体或使用系统字体。分辨率与格式论文插图通常需要dpi300或更高的分辨率格式首选.png无损或.pdf矢量无限放大。信息过载一张图表达一个核心观点。避免在一张图上塞入过多曲线或柱状。使用子图plt.subplots来组织多个相关图表。4. 工程化与文档化构建可复现的求解流程数模竞赛不仅是比模型创意也是比工程实现和文档表达。一个清晰、可复现的代码结构和详尽的文档是加分项。4.1 项目代码结构设计一个优秀的数模项目代码不应是单个巨长的脚本。合理的模块化设计能让调试、协作和后期复查都变得轻松。2021_APMCM_C题_生态保护评价/ │ ├── data/ # 数据目录 │ ├── raw/ # 原始赛题数据 │ ├── processed/ # 清洗处理后的中间数据 │ └── results/ # 模型输出的最终结果数据 │ ├── src/ # 源代码目录 │ ├── data_preprocessing.py # 数据读取、清洗、标准化 │ ├── model_ahp.py # AHP层次分析法实现 │ ├── model_entropy_weight.py # 熵权法实现 │ ├── model_topsis.py # TOPSIS评价模型实现 │ ├── model_sd.py # 系统动力学模型如用Vensim导出数据此处为分析脚本 │ ├── model_nn.py # 神经网络预测模型 │ ├── visualization.py # 所有绘图函数 │ └── main.py # 主程序串联整个流程 │ ├── output/ # 输出目录 │ ├── figures/ # 生成的所有图表 │ └── tables/ # 生成的Latex或Markdown格式表格 │ ├── docs/ # 文档目录可选 │ └── 模型假设与参数说明.md │ ├── requirements.txt # Python依赖包列表 ├── README.md # 项目总说明包括如何运行 └── 最终论文.pdf # 生成的竞赛论文在main.py中流程清晰可见# main.py 示例 from src import data_preprocessing as dp from src import model_ahp, model_entropy_weight, model_topsis, visualization as vis def main(): print(Step 1: 数据加载与预处理...) raw_data, cleaned_data dp.load_and_clean_data(data/raw/problem_c_data.xlsx) print(Step 2: 计算AHP权重...) judgment_matrix np.array([[1, 3, 5], [1/3, 1, 2], [1/5, 1/2, 1]]) # 示例矩阵 weights_ahp, cr model_ahp.ahp_weight(judgment_matrix) print(Step 3: 计算熵权法权重...) weights_entropy model_entropy_weight.calculate_entropy_weight(cleaned_data.values) print(Step 4: 计算组合权重...) # 例如主观权重占0.4客观权重占0.6 alpha 0.4 combined_weights alpha * weights_ahp (1-alpha) * weights_entropy combined_weights combined_weights / combined_weights.sum() # 归一化 print(Step 5: 进行TOPSIS综合评价...) scores, ranks model_topsis.topsis(cleaned_data.values, weightcombined_weights, impacts[,,-,]) print(Step 6: 生成可视化结果...) vis.plot_scores_bar(scores, cleaned_data.index.tolist(), save_pathoutput/figures/final_scores.png) vis.plot_weight_comparison([weights_ahp, weights_entropy, combined_weights], labels[AHP权重, 熵权权重, 组合权重], save_pathoutput/figures/weights.png) print(所有计算完成结果已保存至output目录。) if __name__ __main__: main()4.2 论文写作与程序输出的衔接数模论文中的表格和图表应尽可能由程序自动生成以保证数据一致性。例如TOPSIS的中间计算过程标准化矩阵、正负理想解距离和最终排名可以输出为LaTeX格式的表格代码直接粘贴到论文中。def generate_latex_table(data_df, scores, ranks): 生成LaTeX格式的结果表格代码 latex_str \\begin{table}[H]\n\\centering\n\\caption{生态保护方案TOPSIS评价结果}\n\\label{tab:topsis_result}\n latex_str \\begin{tabular}{|c|c|c|c|}\n\\hline\n latex_str 方案 TOPSIS得分 $C_i$ 排名 推荐等级 \\\\\\hline\n for i, (idx, row) in enumerate(data_df.iterrows()): grade 强推荐 if ranks[i] 1 else 推荐 if scores[i] 0.6 else 备选 latex_str f{idx} {scores[i]:.4f} {ranks[i]} {grade} \\\\\\hline\n latex_str \\end{tabular}\n\\end{table} with open(output/tables/topsis_result.tex, w, encodingutf-8) as f: f.write(latex_str) print(LaTeX表格代码已生成。)5. 常见问题与实战调试经验在实际的96小时竞赛中几乎一定会遇到各种预料之外的问题。以下是一些典型问题及我们的解决思路。5.1 数据相关难题问题数据量纲不统一有的指标是百分比0-1有的指标是具体数值如成本单位是万元直接用于综合评价会导致量纲大的指标主导结果。解决必须进行数据标准化/归一化。TOPSIS常用向量归一化熵权法常用Min-Max归一化。务必在整个模型中使用同一种归一化方法的前后一致的数据。问题数据存在缺失值或明显异常值如植被覆盖率大于1。解决对于缺失值根据情况使用均值、中位数、插值法或直接删除。对于异常值需要结合背景知识判断是录入错误还是真实情况可采用箱线图识别并用上下限如1.5倍IQR进行截断或视为缺失值处理。5.2 模型实现与计算问题问题AHP判断矩阵一致性检验不通过CR0.1。解决这是最常见的问题之一。首先检查判断矩阵是否满足互反性a_ij 1/a_ji。如果不通过需要回溯调整专家的打分。在实践中我们编写了一个辅助函数通过微调矩阵中矛盾最突出的元素例如调整导致最大特征值偏离的元素自动寻找满足一致性要求的近似矩阵。问题TOPSIS计算结果区分度不大所有方案得分很接近。解决检查权重向量是否过于平均导致所有指标重要性相似。尝试调整主客观权重的组合比例alpha值。更重要的是回顾指标体系的构建是否合理是否存在信息重叠高度相关的指标可以考虑使用PCA主成分分析进行降维提取不相关的综合指标。问题神经网络预测模型训练时损失不下降或过拟合。解决数据再检查确保输入数据已标准化输出数据范围合理。网络简化生态数据量通常不大网络结构应尽可能简单1-2个隐藏层。正则化在sklearn的MLPRegressor中增加alpha参数L2正则化强度或在Keras中添加Dropout层。早停法Early Stopping这是防止过拟合最有效的手段之一监控验证集损失当不再下降时停止训练。5.3 编程与环境问题问题openpyxl读取的日期变成了数字或datetime对象。解决Excel内部用数字存储日期。使用openpyxl时设置data_onlyTrue读取计算值对于日期单元格可以用cell.is_date判断并用cell.value直接获取datetime对象。在pandas中read_excel的parse_dates参数可以指定哪些列解析为日期。问题matplotlib中文显示为方框。解决这是永恒的问题。除了代码中设置字体更一劳永逸的方法是将字体文件路径加入配置。或者在保存图片时指定字体属性plt.savefig(fig.png, dpi300, bbox_inchestight, fontpropertiesfont_prop)。问题团队协作时代码合并冲突或对方环境运行不了我的代码。解决使用requirements.txt严格管理依赖。在项目根目录下运行pip freeze requirements.txt生成清单。队友通过pip install -r requirements.txt一键安装所有包。对于核心算法函数编写单元测试使用unittest或pytest确保修改代码后核心逻辑正确。回顾整个项目从看到赛题时的茫然到确定技术路线的争论再到深夜调试代码的焦灼最后到论文成稿时的如释重负这96小时是对综合能力的极限挑战。这份“全过程文档及程序”的价值不仅在于它提供了几个可运行的脚本和一篇论文更在于它完整呈现了解决一个复杂现实问题的标准化思考路径和工程化实现方法。对于后来者我的建议是不要只关注最终的模型和结果更要仔细研究其中数据处理的细节、模型选择的权衡、以及遇到问题时的调试日志。这些才是真正能让你的数学建模能力从“知道”迈向“做到”的关键。下次参赛前不妨找一个往届赛题尝试用这样一套完整的流程从头到尾做一遍你收获的将远胜于读十篇优秀论文。