尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

数学建模实战:Python实现熵值法进行客观权重分配与综合评价

数学建模实战:Python实现熵值法进行客观权重分配与综合评价 1. 项目缘起为什么在数学建模中熵值法依然是我的首选在数学建模的赛场上面对一堆指标数据如何科学地给它们分配权重是决定模型成败的关键一步。很多新手一上来就想到层次分析法AHP找专家打分构造判断矩阵。这方法当然经典但问题也很明显主观性太强。几个专家意见不一致怎么办自己拍脑袋给的分数靠谱吗尤其是在数据驱动的时代我们手头明明有大量的客观数据为什么不让数据自己“说话”告诉我们哪个指标更重要呢这就是熵值法Entropy Weight Method的魅力所在。我第一次在国赛中用熵值法确定权重是因为题目给了过去十年的经济、环境、社会等多维度面板数据。如果用AHP我们三个队员对“人均GDP”和“森林覆盖率”谁更重要的看法可能完全不同争论半天也难有定论。但熵值法不同它完全基于数据本身的离散程度来判断。一个指标的数据如果波动很大说明它对评价对象的区分能力强包含的信息量就大理应赋予更高的权重反之如果某个指标在所有评价对象上的数值都差不多那它就是个“老好人”指标区分度低信息量小权重自然就低。听起来很玄乎其实道理很简单。想象一下你要给班上的同学根据“考试成绩”和“出勤率”来综合排名。“考试成绩”这个指标有人考90有人考60波动很大它能清晰地把学霸和学渣区分开信息量足权重应该高。“出勤率”呢可能大家都是95%到100%之间相差无几这个指标对最终排名的影响就很小权重应该低。熵值法干的就是这个“度量波动、分配权重”的自动化、客观化工作。这几年Python几乎成了数学建模的“标配”语言其强大的数据处理库如pandas、numpy和清晰的语法让算法的实现变得异常高效。网上能找到的熵值法代码很多但不少都存在隐藏的“坑”比如没有处理数据标准化时可能出现的零值或负值问题没有考虑指标正向化即统一为效益型指标的步骤或者代码结构混乱难以嵌入到更大的建模流程中。我结合多次实战和评审经验将整个流程梳理、优化并封装成了一个健壮、清晰的Python实现。它不仅是一个算法更是一套包含数据预处理、核心计算和结果验证的完整解决方案。2. 熵值法的核心思想从“信息熵”到“指标权重”在深入代码之前我们必须吃透熵值法的原理。这不仅是为了应付论文里的“模型建立”部分更是为了在结果出现异常时能快速定位问题是出在数据上还是算法步骤上。2.1 信息熵度量不确定性的尺子熵Entropy这个概念源于热力学后来被香农引入信息论称为“信息熵”。它用来度量一个系统的不确定性或混乱程度。一个系统越是有序、越可预测它的信息熵就越低反之一个系统越是混乱、充满各种可能性它的信息熵就越高。举个例子你抛一枚均匀的硬币正面和反面出现的概率都是0.5结果最难预测此时的信息熵最大。如果这枚硬币被做了手脚99%的概率出现正面那么结果就很容易预测大概率是正面此时的信息熵就很小。在熵值法中我们把每个评价指标看作一个“信源”。对于一个有m个评价对象、n个评价指标的矩阵我们针对第j个指标来思考这个指标在不同评价对象上的取值分布情况如何如果第j个指标的值在所有评价对象上都差不多比如出勤率都在95%-100%那么这个指标提供的信息量很少不确定性低熵值就大注意这里熵值大代表信息效用值小后面会解释。如果第j个指标的值差异很大比如考试成绩从30到90那么这个指标包含的信息量很丰富不确定性高从信息论角度看其熵值应该小。注意这里容易产生混淆。在信息论中熵大表示不确定性高、信息量大。但在熵值法用于确定权重时我们进行了一个“转换”一个指标的熵越大说明该指标数据的差异程度越小其提供的信息量效用值就越小因此赋予的权重也应越小。核心在于理解“信息效用值” 1 - 熵值。2.2 熵值法确定权重的四步流程理解了信息熵是度量指标数据差异度的工具后我们就可以梳理出标准流程。整个过程可以分解为四个清晰的步骤数据标准化归一化消除不同指标量纲单位和数量级的影响。比如GDP是万亿级空气质量指数是百级直接比较没有意义。常用的方法有极差标准化Min-Max Normalization和Z-score标准化。在熵值法中我们通常使用极差标准化到[0,1]区间因为后续计算概率需要非负值。计算指标比重将标准化后的数据视为“贡献度”计算每个样本在某个指标下的贡献比重。这实际上是在为每个指标构建一个概率分布。计算信息熵根据信息熵公式计算每个指标的信息熵值。计算信息效用值与权重根据熵值计算信息效用值差异系数效用值越大说明该指标越重要最后将所有指标的效用值归一化即得到最终的权重。这个过程完全由数据驱动没有任何主观判断的介入这是它在处理客观数据时的最大优势。下面这张表概括了从原始数据到最终权重的全过程步骤输入核心操作输出目的与注意事项1. 数据预处理原始数据矩阵X(m×n)指标正向化、无量纲化标准化矩阵X_norm统一指标方向均转为效益型消除量纲影响。注意处理可能出现的零值。2. 计算比重标准化矩阵X_normp_ij x_ij / sum(x_i)比重矩阵P(m×n)将数据转化为概率分布形式。需确保分母不为零。3. 计算熵值比重矩阵Pe_j -k * sum(p_ij * ln(p_ij))熵值向量E(1×n)k1/ln(m)为常数保证熵值在[0,1]区间。当p_ij0时规定p_ij*ln(p_ij)0。4. 计算权重熵值向量Ed_j 1 - e_j,w_j d_j / sum(d_j)权重向量W(1×n)d_j为信息效用值差异越大效用越高。权重为效用值的归一化结果。3. Python实现熵值法从零开始的完整代码与逐行解析理论清晰了我们开始动手实现。我将代码分为几个函数确保每一步都清晰可辨并且包含了必要的异常处理和实际建模中容易忽略的细节。3.1 环境准备与数据加载首先确保你的Python环境安装了必要的库pandas用于数据处理numpy用于数值计算。如果没有通过pip install pandas numpy安装。我们假设有一份名为evaluation_data.csv的数据文件其中行代表评价对象如城市、年份列代表评价指标。第一列可能是对象名称后续列为数值型指标。import pandas as pd import numpy as np # 1. 加载数据 def load_data(file_path): 加载评价数据 Args: file_path: 数据文件路径如 evaluation_data.csv Returns: df: pandas DataFrame包含对象名称和指标数据 data_matrix: 纯数值矩阵 (m x n)用于计算 object_names: 评价对象名称列表 indicator_names: 评价指标名称列表 try: df pd.read_csv(file_path, encodingutf-8) except FileNotFoundError: print(f错误文件 {file_path} 未找到。) return None, None, None, None except Exception as e: print(f读取文件时发生错误{e}) return None, None, None, None # 假设第一列是评价对象名称如城市名、年份 object_names df.iloc[:, 0].tolist() # 假设其余列都是评价指标 indicator_names df.columns.tolist()[1:] # 提取纯数值数据矩阵 data_matrix df.iloc[:, 1:].values.astype(float) print(f数据加载成功。共 {len(object_names)} 个评价对象{len(indicator_names)} 个评价指标。) print(f指标包括{indicator_names}) return df, data_matrix, object_names, indicator_names实操心得在实际建模中数据源可能是Excel、数据库或直接生成的数组。这里用CSV举例是因为它最通用。务必在读取后检查数据形状和是否有缺失值df.isnull().sum()。熵值法要求数据是数值型且无缺失如果存在缺失需要根据情况用均值、中位数或插值法填充这一步必须在标准化之前完成。3.2 核心算法实现包含正向化与标准化的健壮版本这是最核心的部分。我实现了一个函数它集成了指标类型判断、正向化、标准化和熵权计算。def entropy_weight_method(data_matrix, indicator_typesNone): 熵值法计算指标权重完整版 Args: data_matrix: 原始数据矩阵形状为 (m个样本, n个指标) indicator_types: 可选列表长度为n。指定每个指标的类型。 pos 表示效益型越大越好 neg 表示成本型越小越好 mid 表示中间型越接近某个值越好 range 表示区间型落在某个区间内最好。 如果为None则默认所有指标均为效益型(pos)。 Returns: weights: 各指标权重向量形状为 (n,) e: 各指标信息熵向量 normalized_matrix: 标准化后的矩阵 result_df: 包含详细计算过程的DataFrame用于验证和论文 m, n data_matrix.shape X data_matrix.copy().astype(float) # 1. 指标正向化如果提供了指标类型 if indicator_types is not None: if len(indicator_types) ! n: raise ValueError(indicator_types 的长度必须与指标数 n 一致。) for i in range(n): if indicator_types[i] pos: # 效益型无需处理 pass elif indicator_types[i] neg: # 成本型取倒数或负向变换。这里使用 max - x 或 1/x推荐使用线性变换。 # 使用 max - x 可以保持数据顺序反转且线性关系 X[:, i] np.max(X[:, i]) - X[:, i] # 注意如果原数据有0取倒数会出错所以不推荐 1/x elif indicator_types[i] mid: # 中间型假设最优值为 mid_value需要额外参数这里简化为示例 # 公式 x 1 - |x - mid_value| / max(|x - mid_value|) mid_value np.mean(X[:, i]) # 示例实际应根据问题确定 abs_diff np.abs(X[:, i] - mid_value) max_diff np.max(abs_diff) if max_diff 0: X[:, i] 1 - abs_diff / max_diff else: X[:, i] 1 elif indicator_types[i] range: # 区间型假设最优区间为 [a, b]需要额外参数 # 这里简化为示例实际需传入a,b a, b np.percentile(X[:, i], [25, 75]) # 示例用四分位距作为区间 M np.max([a - np.min(X[:, i]), np.max(X[:, i]) - b]) X_new np.ones_like(X[:, i]) for idx, x in enumerate(X[:, i]): if x a: X_new[idx] 1 - (a - x) / M if M 0 else 1 elif x b: X_new[idx] 1 - (x - b) / M if M 0 else 1 # 在区间内则为1 X[:, i] X_new else: raise ValueError(f第 {i1} 个指标的类型 {indicator_types[i]} 不被支持。) else: # 默认所有指标为效益型 print(未提供指标类型默认所有指标为效益型(pos)。) # 2. 数据标准化极差法归一化到[0,1] # 防止分母为零给一个极小值epsilon epsilon 1e-10 X_min np.min(X, axis0) X_max np.max(X, axis0) range_val X_max - X_min # 处理常数列所有值相同的情况 range_val[range_val 0] epsilon X_norm (X - X_min) / range_val # 再次确保没有负值或零值为后续取对数做准备 X_norm np.clip(X_norm, epsilon, 1) # 将所有值限制在[epsilon, 1]之间 # 3. 计算第j个指标下第i个样本的贡献度比重 P X_norm / np.sum(X_norm, axis0, keepdimsTrue) # keepdims保持维度便于广播 # 处理由于数值误差导致的P中元素和为不为1的情况按列归一化一次 P P / np.sum(P, axis0, keepdimsTrue) # 4. 计算第j个指标的信息熵值 e_j m P.shape[0] k 1 / np.log(m) # 常数k # 计算 p * ln(p)当p0时定义该值为0 with np.errstate(divideignore, invalidignore): # 先计算ln(P)P中可能有0ln(0)会产生警告和-inf ln_P np.log(P) ln_P[~np.isfinite(ln_P)] 0 # 将-inf和NaN替换为0 temp P * ln_P temp_sum np.sum(temp, axis0) e -k * temp_sum # 5. 计算信息效用值 d_j 和权重 w_j d 1 - e weights d / np.sum(d) # 6. 组装结果DataFrame便于查看和分析 result_dict { 指标: [f指标{i1} for i in range(n)], 信息熵(e): e, 信息效用值(d): d, 权重(w): weights } result_df pd.DataFrame(result_dict) return weights, e, X_norm, result_df3.3 代码逐行解析与关键坑点让我们深入上面代码的几个关键部分这些地方是新手最容易出错或者现有网络代码常常忽略的。关于指标正向化indicator_types 很多入门教程只讲效益型指标但实际数据中成本型如污染浓度、成本、中间型如PH值、区间型如人体温度非常常见。如果不进行正向化直接标准化那么成本型指标“数值越小越好”的特性会被扭曲导致权重计算完全错误。我的代码提供了四种类型的处理示例。关键在于正向化必须在标准化之前进行因为标准化依赖于变换后的数据范围。关于标准化与零值处理X_norm (X - X_min) / range_val这是极差标准化的公式。问题在于如果某个指标在所有样本上的值都相同即X_max X_min那么range_val为0会导致除零错误。我通过range_val[range_val 0] epsilon来避免并将该指标标准化后的值全部设为0实际上因为分子也为0。但更重要的是后续计算比重P时如果一列全是0或同一个非零常数这列的比重p_ij会相等导致该指标的熵值e_j达到最大值1信息效用值d_j为0最终权重为0。这是符合逻辑的一个没有差异的指标确实不应该影响综合评价结果。关于计算信息熵时的对数处理e_j -k * sum(p_ij * ln(p_ij))是核心公式。当p_ij为0时ln(0)是负无穷0 * ln(0)在数学上被定义为0。代码中with np.errstate(...)上下文管理器临时屏蔽了除以零或对零取对数的警告然后通过ln_P[~np.isfinite(ln_P)] 0将非有限值-inf, nan替换为0最后计算temp P * ln_P。这是数值计算中处理边界条件的标准做法。一个重要的检查点 计算完成后务必检查信息熵e的值是否都在 [0, 1] 区间内。理论上k1/ln(m)保证了这一点。如果出现负值或大于1一定是前面的比重计算或对数处理出了问题。3.4 综合得分计算与结果可视化得到权重后我们通常需要计算每个评价对象的综合得分并进行排序。def calculate_score_and_rank(normalized_matrix, weights, object_names): 计算综合得分并排名 Args: normalized_matrix: 标准化后的矩阵 (m x n) weights: 指标权重向量 (n,) object_names: 评价对象名称列表 (m,) Returns: score_df: 包含得分和排名的DataFrame # 综合得分 标准化后的值 * 权重然后按行求和 # normalized_matrix 形状 (m, n), weights 形状 (n,) # 利用广播机制直接相乘后求和 scores np.dot(normalized_matrix, weights) # 创建结果DataFrame score_df pd.DataFrame({ 评价对象: object_names, 综合得分: scores }) # 按得分降序排列 score_df score_df.sort_values(by综合得分, ascendingFalse).reset_index(dropTrue) # 添加排名 score_df[排名] range(1, len(score_df) 1) return score_df # 主程序执行示例 if __name__ __main__: # 假设数据文件路径 file_path evaluation_data.csv # 1. 加载数据 df, data_matrix, object_names, indicator_names load_data(file_path) if df is None: exit() # 2. 定义指标类型根据实际情况修改 # 例如假设有5个指标前两个是效益型第三个是成本型后两个是效益型 # indicator_types [pos, pos, neg, pos, pos] # 如果不确定可以先设为None默认全为效益型但务必确认数据含义 indicator_types None # 本例默认 # 3. 计算熵权 weights, e, X_norm, entropy_result_df entropy_weight_method(data_matrix, indicator_types) print(\n 熵值法计算过程 ) print(entropy_result_df.to_string(indexFalse)) print(f\n权重合计{np.sum(weights):.6f}) # 4. 计算综合得分与排名 score_df calculate_score_and_rank(X_norm, weights, object_names) print(\n 综合评价得分与排名 ) print(score_df.to_string(indexFalse)) # 5. 可选可视化 - 权重分布 import matplotlib.pyplot as plt plt.figure(figsize(10, 5)) plt.subplot(1, 2, 1) plt.barh(indicator_names, weights) plt.xlabel(权重) plt.title(各指标权重分布) plt.gca().invert_yaxis() # 让权重大的在上方 plt.subplot(1, 2, 2) plt.barh(score_df[评价对象].head(10), score_df[综合得分].head(10)) # 显示前10名 plt.xlabel(综合得分) plt.title(评价对象综合得分TOP10) plt.tight_layout() plt.show()运行这段代码你将得到清晰的权重结果、每个对象的综合得分及排名以及直观的图表。这完全可以直接粘贴到数学建模论文的“模型求解”部分。4. 实战案例城市绿色发展水平评价为了让你更透彻地理解整个过程我们用一个简化的模拟案例走一遍。假设我们要评价A、B、C、D四个城市的绿色发展水平选取了3个指标X1人均GDP万元效益型越大越好。X2单位GDP能耗吨标准煤/万元成本型越小越好。X3空气质量优良天数比率%效益型越大越好。原始数据如下城市人均GDP (X1)单位GDP能耗 (X2)空气质量优良率 (X3)A12.50.8578B9.81.2065C15.20.6582D11.01.0570第一步数据正向化。X1效益型不变。[12.5, 9.8, 15.2, 11.0]X2成本型使用max - x变换。最大值1.20变换后为[0.35, 0.00, 0.55, 0.15]。注意此时数值越大代表越好能耗越低。X3效益型不变。[78, 65, 82, 70]第二步数据标准化极差法。对正向化后的X1最小值9.8最大值15.2。计算(12.5-9.8)/(15.2-9.8)0.5 同理得到[0.500, 0.000, 1.000, 0.222]对正向化后的X2最小值0.00最大值0.55。得到[0.636, 0.000, 1.000, 0.273]对正向化后的X3最小值65最大值82。得到[0.765, 0.000, 1.000, 0.294]第三步计算比重矩阵P。 以X1列为例标准化后和为 0.5010.2221.722。则A城市在X1指标下的比重为 0.5/1.722≈0.290。依次计算得到完整的P矩阵。第四步计算信息熵e。 m44个城市k 1/ln(4) ≈ 0.7213。 对于X1列计算 sum(p * ln(p)) 0.290ln(0.290)0ln(0)0.581ln(0.581)0.129ln(0.129) ≈ -1.274。 则 e1 -k * (-1.274) ≈ 0.918。 同理计算 e2, e3。第五步计算信息效用值d和权重w。 d1 1 - e1 ≈ 0.082。 假设计算得到 d [0.082, 0.145, 0.091]。 权重 w d / sum(d) [0.082, 0.145, 0.091] / (0.0820.1450.091) ≈ [0.257, 0.456, 0.287]。解读单位GDP能耗X2的权重最高0.456说明在这个数据集中四个城市在能耗上的差异最大这个指标对区分城市绿色发展水平贡献的信息量最多。人均GDPX1的权重相对较低。第六步计算综合得分。 将每个城市标准化后的数据乘以权重并求和。 城市A得分 0.5000.257 0.6360.456 0.765*0.287 ≈ 0.642。 同理计算B、C、D最终排名可能是 C A D B。这个结果是否合理我们可以直观判断城市C在人均GDP和空气质量上都是第一能耗也是第二好变换后数值大综合第一是合理的。城市B各项都垫底综合最后也是合理的。这说明熵值法得出的权重和排序符合数据本身的特征。5. 熵值法的局限、改进与在建模中的定位没有任何一个模型是万能的熵值法也不例外。清楚它的边界才能正确使用它。5.1 主要局限性对数据分布敏感熵值法极度依赖样本数据。如果换一批城市或者某个指标的测量尺度发生变化权重结果可能会剧烈波动。它反映的是当前数据集下各指标的区分能力而非指标的绝对重要性。缺乏横向可比性不同评价体系即使指标相同计算出的权重不能直接比较因为权重是相对于当前参与评价的样本集而言的。可能违背常识有时会出现某个明显重要的指标如“安全事故数”因为所有样本数据都为0或都很接近导致权重为0或极低。这时就需要建模者根据专业知识进行人工修正或结合其他方法。无法处理指标相关性如果两个指标高度相关如“研发人员数量”和“研发经费投入”它们所反映的信息有重叠熵值法会重复计算这部分信息导致权重分配失真。5.2 常用改进与变体在实际建模中我们常采用以下策略来增强熵值法的可靠性组合赋权法这是最有效的策略之一。将熵值法客观赋权与层次分析法AHP或德尔菲法主观赋权结合。例如可以按一定比例如客观权重占70%主观权重占30%进行加权综合兼顾数据的客观规律和专家的先验知识。公式可以是组合权重 α * 熵权 (1-α) * AHP权重其中α需要根据具体问题确定。基于相关系数的修正先计算指标间的相关系数矩阵如果两个指标相关系数超过阈值如0.8则对它们的熵权进行惩罚性调整降低其总权重以避免信息重复计算。动态熵权法对于面板数据多年份、多地区可以逐年或分地区计算熵权观察权重随时间或空间的变化趋势这本身就是一个有价值的分析点。5.3 在数学建模论文中的正确“打开方式”在论文中熵值法不应只是一个孤立的“黑箱”代码。你需要清晰地展示其逻辑链条问题分析部分阐述为什么选择客观赋权法指出主观赋权法在本问题数据条件下的不足。模型建立部分给出熵值法的数学公式和计算步骤就像本文第二部分那样。特别说明数据预处理过程正向化、标准化方法的选择及原因。如果进行了改进如组合赋权需详细说明改进的原理和步骤。模型求解部分附上核心计算代码可放在附录。以表格形式展示中间过程如标准化后的数据、各指标的信息熵(e)和信息效用值(d)。这比只给一个最终权重表更有说服力。展示最终权重结果并进行分析“权重结果显示XX指标的权重最高达到0.XXX这表明在该评价体系中各样本在XX指标上的差异最为显著该指标对综合评价值的贡献最大。”模型检验部分灵敏度分析微调原始数据如在合理范围内上下浮动5%观察权重和排序是否发生剧烈变化。如果变化平稳说明模型稳健性好。对比分析用另一种客观赋权法如CRITIC法、主成分分析法也算一遍对比权重结果的异同并讨论产生差异的原因。如果结论一致则大大增强了结果的可信度。个人经验之谈在时间紧迫的数学建模比赛中熵值法因其实现简单、结果直观是快速构建综合评价模型的利器。但我强烈建议不要把它作为唯一的权重确定方法。在论文中即使你主要使用熵值法也最好提一下其他方法如AHP作为对比或备选并简要讨论为什么熵值法在本案例中更合适。这体现了你对方法论的全面思考是论文的加分项。最后永远记住再好的模型也只是工具对结果的专业解释和合理性论证才是论文脱颖而出的关键。
返回列表