简介这套代码基于TOPSIS优劣距离法实现带权重的多目标决策分析以MATLAB脚本方式提供面向需要处理方案评价、项目比选、绩效评估等任务的科研人员和工程师。权重体现各评价指标的重要程度引入后能避免等权假设带来的偏差更符合实际决策偏好也是该实现的核心亮点。代码覆盖完整流程数据预处理、极小型与中间型指标正向化、权重矩阵构建、加权后理想解与反理想解距离计算、相对贴近度排序等。资源包共8个文件包含5个M函数脚本、2个xlsx数据表、1个mat数据文件整体体积仅19KB轻量易用其中脚本按功能拆分主程序与正向化处理分离数据文件为20条河流水质情况Excel与mat格式可直接运行并对照结果验证。已有1946人学习下载适合需要掌握加权TOPSIS建模流程、理解权重对评价结果影响的开发者与学生也可作为模板迁移到自己的多准则评价项目中。1. TOPSIS优劣距离法加权重权从哪来、乘在哪一步决定排名准不准TOPSIS优劣距离法也叫TOPSIS法是综合评价中最常用的打分排序框架先构造一个最优的正理想解和一个最差的负理想解再根据每个方案到两端的距离算贴近度谁离好的近、离坏的远谁就排前面。这个框架本身不带权重但真实评价场景里“指标重要性”从来绕不开于是就有了标题里的问题加入了权重之后代码该怎么改权重该用熵权法算还是直接用业务方拍脑袋的值权重该乘在归一化之后还是之前下面按这个顺序把带权TOPSIS拆开给出能直接跑的Python实现并附上手算验证和踩坑记录适合正在写综合评价代码、准备论文或做选型评分的人。2. 从原始矩阵到贴近度带权TOPSIS的四步数学链路2.1 指标正向化成本型、中间型指标统一成“越大越好”TOPSIS的理想解是“每列取最大值”“越大越好”是隐含前提。但现实数据里误码率、故障率、时延这种成本型指标越低越好pH值、温度这种中间型指标落在最优区间最好不处理直接跑TOPSIS排名会整体出错。成本型指标我常用的处理是极差翻转X max(X) - X。这样保持线性关系结果非负对后续熵权法也友好。取负法X -X虽然也能用但负值带到极差归一化和熵权计算里容易出NaN不推荐。倒数法X 1/X会压缩大数、放大零附近的值除非数据本身有明确物理含义一般也不推荐。中间型指标用对称线性变换X 1 - |X - X_best| / max(|X - X_best|)。它把“离最优值越近”映射到接近1最远映射到接近0。注意当所有样本值都等于最优值时分母为0要给保护分支。def positive_transform(df, cost_colsNone, mid_valsNone): 把成本型、中间型指标统一转成效益型越大越好。 df: 决策矩阵行为方案列为指标 cost_cols: 成本型指标列名越小越好 mid_vals: 中间型指标最优值字典如 {温度: 25} x df.copy() for col in cost_cols or []: x[col] x[col].max() - x[col] # 极差翻转线性且非负 for col, best in (mid_vals or {}).items(): dist (x[col] - best).abs().max() if dist 0: x[col] 1 - (x[col] - best).abs() / dist else: x[col] 1.0 # 全部等于最优值映射为1 return xcost_cols里列名要和df完全一致否则Pandas会报KeyError。mid_vals的best填的是“最优数值”比如温度指标的最优值25不是区间。区间型指标比如“保持在10到20之间最好”可以取区间中点当best近似处理或者先做区间映射再进这个函数。2.2 归一化TOPSIS用向量归一化熵权法用极差归一化归一化解决的是量纲问题带宽单位是Mbps时延单位是ms直接丢进距离公式会把“带宽”的数值尺度放大。TOPSIS经典做法是向量归一化z_ij x_ij / sqrt(sum_k x_kj^2)也就是说每列都除以该列的L2范数使每列平方和为1消除量纲同时保留列内相对关系。这样计算欧氏距离时各列不再受原始单位影响几何意义明确。极差归一化则把所有值压到[0,1]区间公式是 z_ij (x_ij - min_j) / (max_j - min_j)。它最大的价值是给熵权法提供非负输入。因为熵权法里要算p_ij * ln(p_ij)p_ij出现负值会直接算不出对数。所以我的习惯是熵权法用极差归一化TOPSIS的距离计算用向量归一化两个环节各用各的。归一化方式公式适用范围注意点向量归一化x / sqrt(Σx²)TOPSIS距离计算值不落在[0,1]保留相对尺度极差归一化(x-min)/(max-min)熵权法、正向指标受离群值影响线性拉伸有一点要提醒极差归一化里min和max来自样本本身。如果后续会新增方案新方案的取值可能跑出[0,1]导致之前算的权重和理想解失效。做定时评估轮换时最好固定训练样本的min/max新样本用同一组参数变换。2.3 加权与理想解权重必须乘在归一化之后权重唯一的正确位置是归一化之后、计算距离之前。加权决策矩阵 V_ij w_j * z_ij。为什么不能乘在归一化之前因为归一化是按列缩放的先乘权重再归一化每一列除以自己的L2范数权重的比例关系会被列自身的尺度差异“吃掉”。极端情况下你把权重从1改成100归一化后列向量方向不变理想解坐标的排序结果几乎不变权重形同虚设。提示权重之和不归一成1也能算但建议归一化到和为1这样w_j可以解释成该指标在总评价中的贡献比例后续做敏感性分析也更方便。加权之后正理想解就是每一列的最大值V_j max_i(V_ij)。因为正向化后所有指标都是效益型负理想解取每列最小值。每个方案到正负理想解的距离用欧氏距离D_i sqrt(sum_j (V_ij - V_j)^2)D-_i sqrt(sum_j (V_ij - V-_j)^2)贴近度 C_i D-_i / (D_i D-_i)。C越接近1说明方案越贴近理想解排名越靠前。这个式子里权重w_j的数值会直接影响V_ij的坐标位置所以权重列的可靠性决定了排名结果的说服力。3. 用Python实现熵权法TOPSIS权重计算与排序一套跑通3.1 熵权法求权重数据自己说话不用拍脑袋权重的来源分主观和客观两条线。主观权重用AHP层次分析法或专家打分适合指标重要性有业务共识的场景客观权重里最常见的算法就是熵权法。熵权法的逻辑一句话某个指标在方案之间差异越大信息量越大权重越高如果所有方案在这个指标上都差不多说明它对区分排名没有贡献权重就该低。熵权法计算过程分四步正向化极差归一化计算列的概率分布p_ij和熵值e_j最后用差异系数归一化得到权重。代码import numpy as np import pandas as pd def entropy_weight(df, cost_colsNone, mid_valsNone): 熵权法输入原始决策矩阵返回每列权重和为1。 x positive_transform(df, cost_colscost_cols, mid_valsmid_vals) # 极差归一化保证非负且落在[0,1] x_norm (x - x.min()) / (x.max() - x.min()) # 列归一化成概率分布 col_sum x_norm.sum(axis0) p x_norm / col_sum # 信息熵注意0*log(0)按0处理 n len(df) log_p np.zeros_like(p) mask p 0 log_p[mask] np.log(p[mask]) e - (p * log_p).sum(axis0) / np.log(n) # 差异系数转权重 g 1 - e w g / g.sum() return w注意两个参数。第一np.log(n)里的n是方案数不是指标数用错ln底会让所有熵值整体偏移权重比例却可能“歪打正着”但别再赌这个。第二p 0的mask是必须的极差归一化后若有样本刚好落在最小值p就是00*log(0)是NaN不挡的话这一列权重全完。用mask把0位置直接置0干净利落。还有个业务细节熵权法完全由数据驱动它不管“安全性”是不是比“成本”更重要。所以实际项目里更常见的做法是“主客观组合权重”先用AHP或专家打分给一组主观权重再和熵权重按0.5:0.5加权归一化。你要是评委就用组合权重别把决策权全交给数据。3.2 带权TOPSIS主流程正向化、归一化、距离、贴近度有了权重TOPSIS主流程就四步。先正向化再向量归一化再乘权重最后算距离和贴近度。一个函数全包def topsis_with_weight(df, weights, cost_colsNone, mid_valsNone): 带权TOPSIS返回每个方案的贴近度和降序排名。 x positive_transform(df, cost_colscost_cols, mid_valsmid_vals) # 1. 向量归一化消除量纲 z x / np.sqrt((x ** 2).sum(axis0)) # 2. 乘权重weights必须与df列对齐 v z * weights # 3. 正理想解与负理想解 v_plus v.max(axis0) v_minus v.min(axis0) # 4. 欧氏距离 d_plus np.sqrt(((v - v_plus) ** 2).sum(axis1)) d_minus np.sqrt(((v - v_minus) ** 2).sum(axis1)) if (d_plus d_minus).sum() 0: raise ValueError(所有方案完全相同TOPSIS无区分度) score d_minus / (d_plus d_minus) return score, score.rank(ascendingFalse) # 分数越大排名越靠前这里weights最好传pandas.Series列索引和df一致。如果传numpy数组靠位置对齐一旦列顺序变了权重就错位了而且这种错位一般不会报错只会让结果悄悄变错。传Series时Pandas按列名对齐列乱序也不怕。第4步里加了一个除零保护。全部方案完全相同时d_plus和d_minus都是0不拦一下返回的全是NaN。这种情况在实际数据里很少见但自动化跑批时撞上一次就要折腾半天。3.3 一份4行2列的迷你矩阵手算一遍代码对不对立刻知道完整数据里手算太痛苦我习惯先用一个能心算的小例子验证代码再上真实数据。这里用“收益越高越好、成本越低越好”两个指标demo pd.DataFrame({ 收益: [10, 20, 30, 40], 成本: [8, 4, 6, 2], }) w pd.Series({收益: 0.5, 成本: 0.5}) score, rank topsis_with_weight(demo, w, cost_cols[成本]) print(score.round(3)) print(rank)正向化后成本列为 [0, 4, 2, 6]向量归一化后收益列为 [0.183, 0.365, 0.548, 0.730]成本列为 [0, 0.535, 0.268, 0.802]乘0.5权重后正理想解是(0.365, 0.401)负理想解是(0.091, 0)。手算贴近度结果和代码输出一致方案贴近度C排名收益10成本80.0004收益20成本40.5552收益30成本60.4453收益40成本21.0001这里有个值得注意的现象收益30成本6的方案收益更高但排名反而不如收益20成本4的。原因在于TOPSIS看的是“到理想解的整体距离”收益20成本4在二维坐标上离正理想解更近它更均衡。这是TOPSIS的特性不是bug你向业务方解释排名时得提前说明这一点。4. 带权TOPSIS的避坑清单四个坑从现象到解决4.1 成本型指标忘了正向化排出来的结果全是反的现象误码率是成本型指标算出来误码率最低的方案贴近度反而垫底。如果带权TOPSIS的排名和你对业务的直觉大面积冲突先检查是不是有指标方向没转。原因TOPSIS的正理想解取每列最大值。成本型指标不翻转的话最大值对应最差水平理想解本身就是错的距离和贴近度跟着全错。解决把成本列名明确传给cost_cols参数让正向化统一处理。我见过太多人只把权重写好忘记把成本列传进去。推荐在函数入口加一行断言assert set(cost_cols or []) set(df.columns), cost_cols里有不在df中的列4.2 熵权法遇到0值一列NaN拖垮全部权重现象运行entropy_weight返回的权重全是NaN或者某些列权重异常大。原因极差归一化后列最小值对应的样本p0p * log(p)在Python里算出NaNNaN会顺着sum扩散到熵值再扩散到权重。另一个常见触发点是数据里有负值负p直接导致log参数为负。解决先正向化再极差归一化保证输入非负计算里用mask把p0的位置跳过。如果归一化后某列分母(max-min)为0说明这一列所有方案值相同直接删掉该列再算不要让它进权重计算。4.3 权重乘在归一化之前排名为什么“纹丝不动”现象把权重从0.3调成0.9重跑一遍排名完全没变。不是程序没生效是权重被归一化“对冲”了。原因先乘权重再做列归一化每列除的是自身的平方和。权重w_j在分子和分母里同时出现列向量的方向不变归一化后的相对位置也就不变。最终正理想解的坐标照样按每列最大值取排名自然不变。解决严格按“归一化→乘权重→算距离”的顺序写。这也是本函数把权重乘在z这一步之后的用意。调试时想确认权重是否生效可以打印加权矩阵v看不同方案的坐标是否确实被拉伸。4.4 指标列全相等熵权法直接把这一列的权重打成0现象某个指标所有方案取值都一样熵权法算出来权重为0业务方无法接受说“这个指标明明很重要”。原因熵权法衡量的是区分度全相等表示这列信息量为0算法上无法给区分度高的权重。但业务视角里它可能是一个“门槛指标”不达标就不能参与评价。这种指标不适合用熵权法处理。解决两种做法。第一种先把门槛指标单独做硬性过滤不达标的方案直接剔除剩下数据里不再包含该列。第二种用主观权重重置这一列例如专家给定0.2再做组合权重。千万别硬塞进熵权法然后拿着0权重的结果去汇报。5. 给权重排名验个底扰动敏感性与结果可信度检查权重是估计值尤其是熵权法这类客观权重换一批样本可能就变了所以交付前最好做一次敏感性检查成本很低但说服力提升很大。做法就是给每个权重加一个随机扰动例如±10%重新归一化让权重总和保持为1反复跑几百次带权TOPSIS记录每次排名。如果排名大面积变动说明评价结果对“权重的微小变化”很敏感结论不可靠。def sensitivity_check(df, base_w, cost_colsNone, noise0.1, trials200): 基础权重上做随机扰动返回各方案在trials次模拟中的平均排名。 rng np.random.default_rng(42) rank_records [] for _ in range(trials): w base_w * (1 rng.uniform(-noise, noise, sizelen(base_w))) w w / w.sum() # 权重归一化保持可比 _, rank topsis_with_weight(df, w, cost_colscost_cols) rank_records.append(rank.values) res pd.DataFrame(rank_records, columnsdf.index).apply( lambda col: col.mean(), axis0 ) return res.sort_values()这个函数返回每个方案在扰动下的平均排名。平均排名和原排名差1位以内正常差2位以上就说明这个排名高度依赖权重取值汇报时要给出排名区间而不是唯一名次。除了数值检查我还会把加权矩阵v和正负理想解坐标导出成Excel核一遍极端方案。正理想解的每个坐标有没有落在合理范围负理想解是不是真的代表“最差组合”这些一眼能看出来的逻辑错误往往比算法细节更致命。现在做评价类项目我一般会同时输出三样东西原始权重、扰动后的平均排名、以及一份“权重取极端值时哪些方案会互换名次”的记录。这三样攒成一个附件评审会上争议会少很多。这个习惯救过我太多次也希望它能帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?