资讯详情

TOPSIS多指标综合评价实战:正向化、组合赋权与稳健性校验

📅 2026/10/24 6:47:38 | 华诺云谱 👁 阅读
TOPSIS多指标综合评价实战:正向化、组合赋权与稳健性校验
简介面向多准则决策方法学习与软件、插件等方案评估场景这套Topsis综合评价法配套代码与数据适合具备一定MATLAB基础、希望把算法流程落到实处的读者兼顾方法理解与代码实践。压缩包共2个文件包含1个xlsx评价数据表与1个m脚本整体约10KB数据表用于承载待评方案与评价指标构成的原始矩阵脚本则对应数据标准化、权重设置、正负理想解构建、距离计算和综合排序等关键环节。已有643人学习表明该案例在课程实验、项目练习与方案比选中具有一定参考价值。读者可借助示例数据复现从指标预处理到贴近度排序的完整分析过程理解定量与定性指标如何统一尺度、权重变化如何影响最终决策并为进一步迁移到产品选择、投资评估等实际场景提供可运行的起点。1. 多指标排序TOPSIS 综合评价法是最容易被低估的一把工具把十台设备的六项检测数据摆到一张表里谁是第一问题听着简单真动手就发现麻烦寿命越长越好、故障率越低越好可温度这类指标的最优点落在某个中间值上偏左偏右都算差。TOPSIS 综合评价法干的正是这件事——把所有指标拉成同一个方向、抹掉量纲、按权重算距离最后输出一个 0 到 1 的贴近度分数。这份 TOPSIS 综合评价法代码及其数据把正向化、标准化、赋权、贴近度四步拆成可单独调用的函数附一份能直接跑通的示例数据。适合要交评价类课程设计的学生、做方案比选或供应商打分的工程师以及手上攒了一堆指标却不知道怎么合成一个分数的人。2. 指标正向化与向量标准化四类指标的方向修正与量纲抹平拿到原始表的第一件事不是写代码是逐列问一句这一列到底是大好还是小好。答不上来后面全是白算。这套代码包把判断外置成一张配置表指标类型、参数都在表里声明代码只负责执行。好处是新增一个指标只是加一行配置不用改函数——插件式的思路比硬编码在一堆 if 里强太多。2.1 极小型、中间型、区间型的转换公式与参数TOPSIS 只接受越大越好的矩阵所以其余三类都要先翻转过来。极小型用列最大值减自身简单粗暴但有效中间型要先给一个最优值best用1 - |x - best| / M度量偏离程度M 取该列偏离量的最大值区间型要给一个最优区间[low, high]落在区间内记 1 分落在区间外按到最近端点的距离衰减。指标类型业务含义需要的参数转换后的取值极大型benefit产量、得分、覆盖率无原值极小型cost故障率、成本、耗时无0 到 (max-min)中间型middle温度、pH、电压best0 到 1区间型interval响应时长、含水率low、high0 到 1真正容易翻车的是 M 的取法。M 用样本自身的极差意味着这一列里只要有一个离群点其余样本全被压扁到 0.9 以上区分度瞬间消失。常见做法是先做异常值检查或者把 M 固定成业务上可接受的容差让分数带上绝对含义。这个选择会一路传到权重上不能随手。import numpy as np import pandas as pd def to_positive(x, kind, **kw): 把各类指标统一翻成极大型越大越好 kind: benefit 极大型 / cost 极小型 / middle 中间型 / interval 区间型 x np.asarray(x, dtypefloat) if kind benefit: return x.copy() if kind cost: return x.max() - x # 极小型列最大值减自身 if kind middle: best kw[best] # 中间型业务认定的最优值 M max(np.abs(x - best).max(), 1e-12) # 1e-12 防止整列等于 best 时除零 return 1 - np.abs(x - best) / M if kind interval: low, high kw[low], kw[high] # 区间型最优区间左右端点 M max(low - x.min(), x.max() - high, 1e-12) y np.ones_like(x) y[x low] 1 - (low - x[x low]) / M y[x high] 1 - (x[x high] - high) / M return y raise ValueError(f未知指标类型: {kind})四个分支对应四种业务语义kw只承载该类型需要的参数传错了会直接抛 KeyError比算出个错的数强。1e-12这个下限是血泪经验某次一整列温度都等于设定值 bestM 变成 0整列除出 nan最后排名表全空排查了半小时才定位到这一行。配置表和执行层分开写指标增删就不用动算法INDICATOR_CFG [ (产量, benefit, {}), (故障率, cost, {}), (温度, middle, {best: 68}), (响应时长, interval, {low: 2, high: 5}), (能耗, cost, {}), (合格率, benefit, {}), ] def build_positive_matrix(df): 按配置表把原始 DataFrame 逐列正向化列顺序与配置一致 cols {} for name, kind, kw in INDICATOR_CFG: if name not in df.columns: raise KeyError(f数据里缺少指标列: {name}) cols[name] to_positive(df[name].values, kind, **kw) return pd.DataFrame(cols, indexdf.index)INDICATOR_CFG是唯一需要业务方确认的地方把它交给提出指标的人签字比事后争论排名合理得多。列顺序决定后面权重向量的顺序所以配置表和权重必须严格对齐我一般在生成权重后立刻断言len(w) len(INDICATOR_CFG)。2.2 为什么用向量归一化而不是 min-max正向化只解决了方向量纲还在。产量以千为单位、合格率是小数直接算欧氏距离产量那一维会把其余全部吃掉。归一化的选择有两种常见路线min-max 把每列压到[0, 1]向量归一化则是每列除以自身的平方和开方得到单位向量。差异在异常值上体现得最明显。min-max 的上下界完全由样本极值决定一个离群点就能把正常样本挤成一条窄带向量归一化保留了数值之间的比例关系对极值没那么敏感。另一个隐性理由是熵权法要求矩阵非负向量归一化天然满足min-max 在含负值的列上还得额外平移多一步就多一个出错的地方。def vector_standardize(pos_df, eps1e-9): 按列做向量归一化x / sqrt(sum(x^2))返回同形状 DataFrame X pos_df.values.astype(float) eps # 整体平移规避全零列 norm np.sqrt((X ** 2).sum(axis0)) norm[norm 0] 1.0 # 极端情况兜底避免除零 return pd.DataFrame(X / norm, columnspos_df.columns, indexpos_df.index)eps取 1e-9 是权衡太小挡不住全零列太大会让本来接近 0 的样本被拉平。norm 0的那一行兜底几乎是死代码但留着不亏——数据是别人给的你永远不知道下一版会不会整列填 0。标准化之后的矩阵建议存一份中间结果后面调权重、做敏感性分析都要重复用不必每次从头算。3. 权重定标熵权法、AHP 与组合权重的三套写法权重是这个方法里唯一没有标准答案的部分。同样的数据换一套权重第一名可能就换人。所以争论不该发生在哪个方法对而该发生在哪种权重能反映业务判断。主观权重AHP承载专家偏好客观权重熵权法反映数据自身的区分度两者各有盲区工程上更稳的做法是都算一遍看结论是否一致。3.1 熵权法差异越大权重越大以及它的三个前提熵权法的逻辑是某一列在所有样本上差异越大信息熵越小说明它区分样本的能力越强就该给更高权重。反过来一列数值全都差不多说明它对排序没贡献权重自然被压低。这个逻辑成立有三个前提缺一个结果就会失真。第一矩阵必须非负负值取对数直接崩第二样本量不能太小五六个样本算出来的熵值抖动极大权重基本是随机的第三不能有大量重复值一列里一半样本取同一个数比例被稀释权重会被系统性低估。第三条最隐蔽常见的问卷打分数据全是 1 到 5 的整数重复值一大堆算出来的权重看着正常实际没什么解释力。def entropy_weight(Z): Z: 标准化后的非负矩阵 (n_samples x m_indicators)返回长度 m 的权重 Z np.asarray(Z, dtypefloat) n, m Z.shape P Z / Z.sum(axis0, keepdimsTrue) # 每列转成比重 P np.clip(P, 1e-12, 1.0) # 0 * log(0) 的处理兜底下限 e -(P * np.log(P)).sum(axis0) / np.log(n) # 信息熵除以 ln(n) 归一 d 1 - e # 差异系数 return d / d.sum()np.log(n)是归一化因子让熵值落在 0 到 1 之间样本量变化时权重的量级才不会飘。np.clip那一行是必须的标准化后出现 0 很正常极小型指标翻转后最小值恰好为 0不夹一下就是nan传染全表。算完建议打印一下e和d如果某一列的差异系数接近 0说明它没什么区分度可以拿去和业务方确认是否该保留。3.2 AHP 判断矩阵与一致性校验CR 超过 0.1 就重填主观权重的标准做法是层次分析法核心是一个两两比较矩阵指标 i 相对指标 j 有多重要用 1 到 9 的标度打分。矩阵必须满足互反性a_ji 1 / a_ij理想情况下还应满足一致性也就是i 比 j 重要 2 倍、j 比 k 重要 3 倍时i 应该比 k 重要 6 倍。人脑填不出这种一致性所以要算 CR 来量化偏差。RI_TABLE {1: 0.0, 2: 0.0, 3: 0.58, 4: 0.90, 5: 1.12, 6: 1.24, 7: 1.32, 8: 1.41, 9: 1.45} def ahp_weight(A): A: 互反判断矩阵 (m x m)返回 (权重, 最大特征值, CI, CR) A np.asarray(A, dtypefloat) m A.shape[0] eigvals, eigvecs np.linalg.eig(A) k int(np.argmax(eigvals.real)) # 最大实特征值对应的位置 lam eigvals[k].real w np.abs(eigvecs[:, k].real) # 特征向量取模规避符号问题 w w / w.sum() CI (lam - m) / (m - 1) if m 1 else 0.0 RI RI_TABLE.get(m, 1.45) CR CI / RI if RI else 0.0 return w, lam, CI, CRnp.abs不能省特征向量的符号是任意的不取模会出现负权重。RI_TABLE只到 9 阶超过 9 个指标就该考虑分层硬塞进一个矩阵人会填糊涂。CR 大于 0.1 时不要自己微调数字凑通过那是自欺——回到指标定义上重新想清楚谁更重要通常是有两个指标语义重叠了。3.3 组合权重乘法合成还是线性加权主观和客观都算出来之后合成方式有两种。乘法合成是先乘再开方归一对两者都低的值惩罚更重线性加权各取一半谁高谁就拉得住。工程上我偏好乘法合成因为它不会让某一方的高权重无条件主导但前提是两套权重都不含 0一旦主观权重里给了 0乘法直接把该指标打死。def combine_weight(w_obj, w_sub, modemultiply): w_obj: 客观权重(如熵权) w_sub: 主观权重(如 AHP) w_obj np.asarray(w_obj, dtypefloat) w_sub np.asarray(w_sub, dtypefloat) if w_obj.shape ! w_sub.shape: raise ValueError(两套权重维度不一致检查指标顺序) if mode multiply: w np.sqrt(w_obj * w_sub) # 几何平均后归一 else: w 0.5 * w_obj 0.5 * w_sub # 线性加权比例可调 return w / w.sum()维度检查这一步救过我一次配置表里插了一个新指标主观矩阵忘了补一列两套权重长度不同合成时 numpy 静默广播出一个诡异的形状最后得分全部错位。从那以后这个断言成了标配。4. 正负理想解与贴近度距离计算、得分归一化与结果落盘权重定了剩下就是几何题在加权标准化空间里人为构造一个各项都最优的虚拟点和各项都最差的虚拟点然后看每个样本离谁更近。离正理想解越近、离负理想解越远得分越高。这套逻辑的好处是结果有明确几何含义不像加权求和那样把不同维度的补偿关系糊在一起。4.1 正负理想解的构造与欧氏距离正理想解是加权矩阵每一列的最大值负理想解是每一列的最小值。注意是列的最大最小不是某个真实样本——这两个点通常不存在于样本中它们只是参照系。这里有个顺序问题必须说清楚先标准化再加权和先加权再标准化结果不一样。标准流程是先标准化得到无量纲矩阵再乘权重因为权重的物理含义是这一维在距离度量里的重要程度加在无量纲的维度上才有意义。反过来的话量纲没消除权重会被数值大小吞掉。def topsis(Z, w): Z: 标准化矩阵 (n x m)w: 权重向量 (m,) 返回 (贴近度 C, 到正理想解距离 D, 到负理想解距离 D-, 加权矩阵 V) V np.asarray(Z, dtypefloat) * np.asarray(w, dtypefloat) # 加权标准化矩阵 v_pos V.max(axis0) # 正理想解每列最大 v_neg V.min(axis0) # 负理想解每列最小 d_pos np.sqrt(((V - v_pos) ** 2).sum(axis1)) d_neg np.sqrt(((V - v_neg) ** 2).sum(axis1)) C d_neg / (d_pos d_neg 1e-12) # 贴近度分母加极小值防除零 return C, d_pos, d_neg, V分母的1e-12只在一种情况下起作用某个样本恰好同时等于正负理想解也就是所有指标在所有样本上完全一样。这时d_pos d_neg 0不兜底就是非法值。4.2 贴近度归一化与排序输出贴近度 C 天然落在 0 到 1 之间越大越好不需要再归一化。但有两个细节值得注意第一C 是相对值只在当前这批样本内部可比加进来一个新样本所有人的 C 都会变跨批次比较没有意义第二排名相同时要指定method默认的平均排名会把两个并列样本排成 2.5 名做报表时不好看一般用min或first。def rank_table(names, C, d_pos, d_neg): 把结果拼成可读的排名表保留中间量便于排查 out pd.DataFrame({ D: np.round(d_pos, 6), D-: np.round(d_neg, 6), 贴近度: np.round(C, 6), }, indexnames) out[排名] out[贴近度].rank(ascendingFalse, methodmin).astype(int) return out.sort_values([排名, 贴近度], ascending[True, False])保留D和D-是有用的两个样本贴近度接近时看它们各自的距离能判断是都很均衡还是一个偏极端这在汇报时经常被追问。4.3 全流程脚本从原始 CSV 到 ranking.csv把前面几块拼起来就是一份可以直接跑的主流程。示例数据放在data/samples.csv第一列是样本名设备编号、方案编号之类其余列与配置表一一对应。import os import numpy as np import pandas as pd raw pd.read_csv(data/samples.csv, index_col0) # 首列为样本名 pos build_positive_matrix(raw) # 第一步正向化 Z vector_standardize(pos) # 第二步向量归一化 w entropy_weight(Z.values) # 第三步熵权可换成组合权重 C, d_pos, d_neg, V topsis(Z.values, w) # 第四步正负理想解与贴近度 out rank_table(raw.index, C, d_pos, d_neg) os.makedirs(output, exist_okTrue) out.to_csv(output/ranking.csv, encodingutf-8-sig) # 带 BOMExcel 打开不乱码 pos.to_csv(output/matrix_positive.csv, encodingutf-8-sig) print(out.head(10)) print(权重:, dict(zip(Z.columns, np.round(w, 4))))utf-8-sig是给 Excel 用的少了它中文列名在 Windows 上开出来就是乱码这种问题交付时最尴尬。中间结果matrix_positive.csv建议一起落盘业务方质疑某个样本分数时顺着这张表往回查比重算一遍快得多。如果权重是 AHP 给的把entropy_weight(Z.values)换成硬编码的数组即可接口形状一致。5. 避坑排查五类让排名悄悄翻车的数据与计算细节TOPSIS 最难的地方不在公式公式就那几行。难在它非常宽容——大部分错误输入不会报错只会给你一个看起来很正常、但完全站不住的排名。下面五条都是实际遇到过的按数据层面和计算层面分开说。5.1 数据层面的三个坑坑一指标方向判断错误。现象排名结果和业务直觉完全相反成本最低的方案排到了倒数第一。 原因某列成本型指标被当成极大型处理没做翻转。正向化配置表是人工填的填错不会触发任何异常代码照样跑完。 解决正向化后立刻做一次符号自检对每一列比较翻转前的最大值行和翻转后的最大值行成本型指标这两行必须不同。更省事的办法是让配置表带一列中文说明交付前逐行念一遍这个指标是大好还是小好。坑二区间型指标的 M 被极值绑架。现象区间型指标那一列除了一两个离群样本其余全部挤在 0.95 以上该指标的权重被熵权法压到接近 0。 原因M 取的是样本自身极差一个偏离区间很远的样本就把 M 撑大其余样本的偏离量相对就变得微不足道。 解决先看这一列的分布离群点用业务规则剔除或单独标注也可以把 M 固定成业务容差比如响应时长超出区间 10 秒即视为 0 分让分数带绝对含义。两种做法的排名会不同选哪种要和提指标的人说清楚。坑三样本量太小导致权重随机。现象去掉一个样本重跑第一名换了人权重表也面目全非。 原因熵权法依赖分布样本数在 5 到 8 之间时熵值估计的方差极大同时正负理想解由样本极值决定少一个极值点参照系就变了。 解决样本量少于 10 时不要单独用熵权法改用 AHP 或等权至少结论可解释。真要用客观权重配合后面的扰动测试把不稳定程度量化出来再说。5.2 计算层面的两个坑坑四先加权再标准化权重被量纲吃掉。现象手工验算某样本的贴近度和脚本结果对不上差距还不小。 原因流程顺序反了。先乘权重再归一化等于把权重的绝对大小也一起归一掉了最终权重只影响方向不影响相对强度和预期完全不符。 解决固定顺序为正向化 → 向量归一化 → 加权 → 算距离把中间矩阵落盘用一个小样本手算三行核对一遍。这类错误不会报错只能靠中间结果对齐。坑五把贴近度当成绝对水平横向比较。现象两个批次的方案分别用同一套代码排名甲批次最高分 0.82乙批次最高分 0.65被解读成乙批次整体更差。 原因C 是相对贴近度分母是当前样本集内的正负理想解距离之和样本集换了参照系就换了数值没有跨批次可比性。 解决跨批次比较要么把两批数据合并成一个矩阵重算要么改用有绝对参照的评价方法。汇报时也要把这是组内相对排名写进口径说明别留模糊空间。提示这五条里坑一和坑四最容易发生也最难自查因为它们都不报错。养成跑完先看中间矩阵的习惯比事后 debug 省时间。6. 排名可信吗三种交叉验证与 ±10% 权重扰动的稳健性测试排名表交出去之后一定会被问一句这个结果靠谱吗。答公式是标准的没有说服力得拿数据说话。我一般做两件事换一种评价方法看排名是否一致再扰动权重看排名是否稳定。两件事都过了才敢把结果放进汇报材料。第一种是排名一致性检验。用加权求和法把加权标准化矩阵按行求和和秩和比法各算一遍排名然后和 TOPSIS 的排名做 Spearman 相关系数。系数在 0.9 以上说明结论对方法不敏感可以放心讲掉到 0.7 以下就得回头看通常是某个指标在这几种方法里的作用方式差异很大比如区间型指标在加权求和里被严重低估。from scipy.stats import spearmanr def weighted_sum_rank(Z, w): 加权求和法的对照排名越大越好 s (np.asarray(Z) * np.asarray(w)).sum(axis1) return pd.Series(s).rank(ascendingFalse).values def consistency_check(Z, w, C): base_rank pd.Series(C).rank(ascendingFalse).values alt_rank weighted_sum_rank(Z, w) rho, p spearmanr(base_rank, alt_rank) return round(rho, 4), round(p, 4)第二种是权重扰动。思路很简单给每个权重乘一个[0.9, 1.1]之间的随机因子再归一化重跑 200 次统计排名完全不变的比例。这个比例高于 0.8说明结论稳低于 0.5说明排名基本由权重噪声决定此时该讨论的不是排名而是权重怎么定。def weight_perturb(Z, w, rounds200, ratio0.1, seed42): 权重在 ±ratio 内随机扰动返回排名完全不变的频率 rng np.random.default_rng(seed) Z np.asarray(Z, dtypefloat) base, *_ topsis(Z, w) base_rank pd.Series(base).rank(ascendingFalse).values hits 0 for _ in range(rounds): w2 np.asarray(w) * rng.uniform(1 - ratio, 1 ratio, sizelen(w)) w2 w2 / w2.sum() # 扰动后必须重新归一 c2, *_ topsis(Z, w2) hits bool((pd.Series(c2).rank(ascendingFalse).values base_rank).all()) return hits / roundsseed固定是为了可复现汇报时别人拿同样的种子能跑出同样的数字。ratio不建议开到 0.3 以上那已经不是扰动而是换权重重算了。真正有价值的是看哪些样本最容易掉名次——把 200 次里的排名分布打出来那些名次在 1 和 3 之间反复横跳的样本就是结论的薄弱环节汇报时主动说明比被追问强。还有一个我常做的检查删一法。每次剔除一个样本重跑整个流程看剩余样本的排名变化幅度。变化超过两位的样本通常是极值点它自己在拉动正负理想解。这类样本要么核实数据要么在报告里单独说明它的特殊性。从那以后我每次交付评价结果都强制走一遍「顺序自检 一致性检验 权重扰动」这三关中间矩阵和扰动分布一并存档。不是流程好看而是被问住过一次之后就知道能被复现的结论才叫结论。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑