数学建模实战:用Python实现EWM-TOPSIS水质评价(附完整代码)
数学建模实战:用Python实现EWM-TOPSIS水质评价(附完整代码)
当20条河流的水质数据摆在面前,如何科学地评价它们的优劣?这个问题困扰着许多环境监测人员。传统的主观赋权方法往往受限于专家经验,而熵权法(EWM)与TOPSIS的组合,为我们提供了一种客观、可量化的解决方案。本文将手把手带你用Python实现这一算法,从原理到代码,彻底掌握水质评价的数学建模全流程。
1. 环境数据预处理:统一评价尺度
水质评价的第一步是解决指标"语言不通"的问题。含氧量越高越好(极大型指标),细菌总数越少越好(极小型指标),PH值接近7最佳(居中型指标),植物性营养物含量在10-20ppm区间最优(区间型指标)。这种指标类型的混杂会直接影响评价结果。
1.1 指标正向化处理
我们需要将所有指标统一转化为极大型指标。Python实现的关键代码如下:
# 最小化指标→极大化 (细菌总数) data["细菌总数_极大化"] = data["细菌总数(个/mL)"].apply( lambda x: max(data["细菌总数(个/mL)"]) - x) # 居中型→极大型 (PH值) PH_M = max(abs(data["PH值"] - 7)) data["PH值_极大化"] = data["PH值"].apply( lambda x: 1 - abs(x - 7) / PH_M) # 区间型→极大型 (植物性营养物) nutrition_M = max(10 - min(data["植物性营养含量(ppm)"]), max(data["植物性营养含量(ppm)"]) - 20) data['植物性营养含量_极大化'] = data["植物性营养含量(ppm)"].apply( lambda x: 1 - (10 - x)/nutrition_M if x < 10 else (1 - (x - 20)/nutrition_M if x > 20 else 1))1.2 数据标准化处理
不同指标的量纲差异会影响距离计算。我们采用向量归一化方法消除量纲:
import numpy as np for column in normalized_data.columns: squared_sum = np.square(normalized_data[column]).sum() normalized_data[column] = normalized_data[column] / np.sqrt(squared_sum)注意:标准化后的数据范围不再是0-1,但保留了各样本间的相对大小关系,这对TOPSIS算法至关重要。
2. 熵权法(EWM):让数据自己说话
熵权法的核心思想是:指标数据波动越大,提供的信息越多,权重就应该越高。这完全由数据自身决定,避免了人为干扰。
2.1 熵权计算四步法
计算特征比重:每个值占该列总和的比例
normalized_data["P_含氧量"] = normalized_data["含氧量(ppm)"].apply( lambda x: x / sum(normalized_data["含氧量(ppm)"]))计算信息熵:衡量数据混乱程度
import math e_1 = - (1/math.log(20)) * sum( normalized_data["P_含氧量"].apply( lambda x: x * (math.log(x + 1e-7))))计算信息效用值:1 - 信息熵
d = [1 - e_i for e_i in e]归一化得到权重:
w = [d_i / sum(d) for d_i in d]
2.2 权重结果解读
假设最终得到的权重为:
| 指标 | 含氧量 | PH值 | 细菌总数 | 植物营养物 |
|---|---|---|---|---|
| 权重 | 0.32 | 0.18 | 0.28 | 0.22 |
这表明在该数据集中,含氧量对水质影响的区分度最大,而PH值的区分度相对较小。
3. TOPSIS算法:寻找最优解
TOPSIS(优劣解距离法)通过计算各样本与理想解的距离进行排序。结合熵权法后,评价结果更具说服力。
3.1 正负理想解确定
# 正理想解:各指标最大值 C_positive = [max(normalized_data[i]) for i in list(normalized_data.columns)] # 负理想解:各指标最小值 C_negative = [min(normalized_data[i]) for i in list(normalized_data.columns)]3.2 加权距离计算
关键是要考虑熵权法得到的权重:
w = np.array(w) # 熵权法权重 normalized_data['dist_to_positive'] = normalized_data.iloc[:,:4].apply( lambda row: np.sqrt(np.sum(np.square((row - C_positive) * w))), axis=1)3.3 相对接近度计算
最终得分公式: $$ f = \frac{d^-}{d^+ + d^-} $$
Python实现:
normalized_data['f'] = (normalized_data['dist_to_negative'] / (normalized_data['dist_to_negative'] + normalized_data['dist_to_positive']))4. 结果可视化与解读
4.1 水质排名柱状图
使用Seaborn绘制带数据标签的水平柱状图:
import seaborn as sns plt.barh(data_show["河流"], data_show["f"], color=colors) for i, (index, row) in enumerate(data_show.iterrows()): plt.text(row['f'], i, f"{row['f']:.2f}", va='center') plt.title("河流水质综合评价得分")4.2 多维特征雷达图
Plotly库可以生成交互式雷达图,直观展示各河流在不同指标上的表现:
import plotly.graph_objects as go fig = go.Figure() for i in range(len(df)): fig.add_trace(go.Scatterpolar( r=[df.iloc[i][col] for col in categories], theta=categories, fill='toself', name=f'河流 - {df.iloc[i]["河流"]}' )) fig.show()4.3 实际应用建议
- 数据质量检查:异常值会严重影响熵权法结果,建议先进行箱线图分析
- 指标敏感性测试:尝试增减指标,观察权重变化是否合理
- 结合实地考察:数学建模结果应与实际水文监测数据相互验证
完整代码已封装为Jupyter Notebook,包含详细的注释和示例数据。在实际项目中,我曾用这个方法成功识别出某流域的潜在污染源,比传统方法提前两周发出水质预警。
