ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TOPSIS综合评价法原理与Python实现:从数据准备到排序实战

TOPSIS综合评价法原理与Python实现:从数据准备到排序实战 简介一份基于Matlab实现的Topsis综合评价法代码包面向需要进行多准则决策分析的数据分析师、算法工程师及相关专业学生解决在多个备选方案中依据多项指标排序选优的问题。资源含2个文件一个Excel数据文件用于存放待评价的基础数据一个Matlab脚本文件实现数据标准化、权重计算、最优/最劣向量构造、距离度量及贴合度指数计算压缩包整体约10KB小巧易用。目前已有637人浏览学习。借助随附的银行数据示例读者可直接运行脚本模拟完整的Topsis分析流程理解每个步骤的数学含义与代码实现并据此替换为自己业务场景的数据。对刚接触综合评价方法或希望快速将Topsis落地为可用工具的开发者而言这份配套资源能极大缩短上手时间把精力更多集中在指标权重设定与结果解读上。 综合评价这件事我做了不少。早几年我也和大多数人一样一提评价就打开SPSS做AHP但后来在实际项目里用得最多的反而是Topsis。原因很朴素不用反复构造判断矩阵不需要掐着指头算一致性比例对数据分布几乎没有要求跑出来的结果还特别好解释。这篇博文不绕弯子把Topsis综合评价法的原理、代码、数据准备一次讲透附一套可以直接运行的Python实现给正在做科研评价、数学建模以及企业选型的朋友做个参考。很多教程讲Topsis喜欢一上来堆公式看完还是不知道怎么用。我的写法反着来先讲清楚这个方法解决什么问题再给你一套能跑通的数据和代码最后把最容易翻车的细节挨个说一遍。1. 为什么大部分综合评价场景都绕不开Topsis1.1 适用场景什么时候选Topsis而不是其他方法Topsis全称是Technique for Order Preference by Similarity to Ideal Solution中文常翻译为“优劣解距离法”。名字很长但核心思想就一句话在一堆候选方案中找出一个理论上“最好”的方案和一个“最差”的方案然后看每个候选对象离谁更近离最好的越近、离最差的越远排名就越靠前。我最早接触这个方法是在数学建模竞赛里后来在科研论文和实际项目中也经常用到。供应商选优、项目选址、区域发展水平评价、水质评价、员工绩效排名这些典型的多指标综合评价场景Topsis都能用。它最大的好处是不需要预设数据的分布形态不管是正态、偏态还是量纲差异极大的数据只要做了归一化就能算这一点比很多统计评价方法友好得多。和AHP、熵权法这些方法比Topsis的定位不太一样。AHP侧重处理“定性判断”需要专家打分构造判断矩阵熵权法本质是算权重不是完整的评价排序方法而Topsis是直接用数据完成从指标到排序的全过程。我做了个简单对比方法是否需要专家打分主要产出适合场景AHP需要权重和排序指标层次多、定性判断多的场景熵权法不需要权重客观权重计算常与Topsis搭配秩和比法不需要排序、分档样本量大、需要分档评价的场景Topsis可选排序多指标决策、方案优选1.2 为什么它的可解释性那么强Topsis的结果容易让人信服核心在于“贴近度”这个输出指标非常直观。贴近度取值范围在0到1之间0.8意味着这个方案距离最优方案比距离最劣方案近得多0.3则说明它和“最差虚拟方案”更接近。给非技术背景的决策者汇报时不需要解释复杂的统计概念直接说“C方案综合贴近度最高是推荐项”对方很快就能理解。这也是我在实际项目中弃用AHP的原因之一。AHP在打分环节往往要来回调整多轮一致性检验不过关时还要返工而Topsis只要指标方向和权重确定了结果就是确定的、可复现的评审质疑时你有据可查。2. 算法核心贴近度到底在算什么2.1 正理想解与负理想解从“最好可能”到“最差底线”理解Topsis关键在“正理想解”和“负理想解”这两个虚拟方案。正理想解就是在每个指标上都取最优值构成的一个假想方案。比如评价供应商时有“质量合格率”“价格”“交货准时率”三个指标质量合格率越高越好、价格越低越好、交货准时率越高越好那么正理想解就是“合格率100%、价格最低、准时率100%”的组合。负理想解正好相反每个指标都取最差值。实际中的方案当然不可能同时满足所有最优但有了这两个虚拟标杆每个方案都能算出一个“距离”一个是到正理想解的欧氏距离D一个是到负理想解的欧氏距离D-。最优方案应该是D尽量小、D-尽量大的那个。这个思路和生活里选房子很像。你看房时心里会有一个“完美户型”和一个“最差户型”然后每套房子都和这两个标杆比离完美越近、离最差越远的自然就是优先级最高的选择。2.2 计算流程五步走完整实现Topsis的标准计算流程可以拆成五步这也是后面写代码的直接依据构造决策矩阵。行是待评价方案列是指标。指标正向化。把越小越好的负向指标转换成一列“越大越好”的数据通常取倒数。向量归一化。对每一列独立归一化消除量纲影响。加权。给每个指标乘上对应权重默认可以等权。计算正负理想解、欧氏距离和贴近度。第2步是最容易出错的环节很多初学者拿到数据直接归一化忘了处理指标方向结果排序完全不合理。这个点我在后面专门展开说。2.3 贴近度公式的直观理解贴近度C的公式是C D- / (D D-)当方案距离负理想解D-很大、距离正理想解D很小时C趋近于1说明这个方案整体表现很好反过来C趋近于0说明这个方案综合表现很差。要注意的是贴近度是一种“相对排名”指标它依赖于当前参与评价的方案集合。同一家供应商放在不同候选池里贴近度数值会变化但这不影响排序逻辑——比较的是相对优劣不是绝对水平。3. 数据准备方向处理与归一化这两个关口3.1 正向指标与负向指标的判定写代码前先把手头的数据过一遍每个指标到底是越大越好还是越小越好成本类、时间类、风险类指标一般是负向指标比如采购价格、售后响应时间、故障率效益类、质量类指标一般是正向指标比如合格率、利润率、产能、技术评分。这一步看着简单但实际项目里经常遇到模糊情况。比如“碳排放量”是负向指标如果评价的是环保绩效那显然是越低越好但如果在某个打分体系里给出的是“环保合规得分”那又是正向的。所以我建议在准备数据时列一张指标方向表明确标注每个指标的正负向后面写代码时把这张表直接映射成impacts参数避免做到一半回头改。3.2 向量归一化 vs 极差归一化为什么标准Topsis用前者归一化有两种常用做法极差归一化Min-Max和向量归一化。我见过不少人在Topsis里用极差归一化结果指标一换排序就变味原因就在这里。Topsis标准方法用的是向量归一化公式是每个元素除以该列所有元素平方和再开方。这样做保留了列向量之间的范数比例关系适合后续继续计算欧氏距离。极差归一化会把每一列强制压缩到0-1区间最大值变成1、最小值变成0虽然也消除了量纲但它改变了列与列之间原有的数值比例关系在Topsis框架下可能导致排序不稳定。一句话结论算Topsis老老实实用向量归一化。如果你是在做其他分析需要极差归一化那是另一回事别混用。3.3 构造一套可运行的供应商示例数据为了演示我准备了一份5家供应商、6个指标的数据集包含质量合格率、价格、交货准时率、售后响应时间、技术能力评分、产能。这份数据我故意选了一些容易出错的组合比如价格是负向指标、售后响应时间也是负向指标方便后面看方向处理的实际影响。import pandas as pd df pd.DataFrame({ 供应商: [A, B, C, D, E], 质量合格率(%): [98.5, 97.2, 99.1, 96.8, 98.0], 价格(万元): [120, 105, 130, 98, 115], 交货准时率(%): [95, 92, 98, 90, 94], 售后响应时间(小时): [24, 18, 30, 12, 20], 技术能力评分: [8.5, 7.8, 9.2, 7.0, 8.0], 产能(万件/月): [10, 8, 12, 6, 9] }) impacts [, -, , -, , ]这份数据可以直接复制到Python里运行也可以替换成自己的真实数据只要保证impacts标签和指标顺序一一对应就行。4. 从零手写Topsis完整Python代码与逐段解释4.1 完整代码我不太推荐直接调现成封装库因为Topsis本身逻辑不复杂自己写一遍能彻底搞清楚每个参数的用途出了问题也容易排查。下面这个函数是我平时最常用的版本依赖只有numpy和pandasimport numpy as np import pandas as pd def topsis(data, weightsNone, impactsNone): data : DataFrame或ndarray一行一个方案一列一个指标 weights: 权重列表默认等权 impacts: 指标方向列表表示越大越好-表示越小越好 arr np.array(data, dtypefloat) n, m arr.shape # 1. 指标正向化负向指标取倒数 if impacts is None: impacts [] * m for j in range(m): if impacts[j] -: arr[:, j] 1 / arr[:, j] # 2. 向量归一化 norm_arr arr / np.sqrt((arr ** 2).sum(axis0)) # 3. 权重处理默认等权 if weights is None: weights np.ones(m) / m else: weights np.array(weights, dtypefloat) weights weights / weights.sum() weighted norm_arr * weights # 4. 正理想解和负理想解 ideal_best weighted.max(axis0) ideal_worst weighted.min(axis0) # 5. 欧氏距离 dist_best np.sqrt(((weighted - ideal_best) ** 2).sum(axis1)) dist_worst np.sqrt(((weighted - ideal_worst) ** 2).sum(axis1)) # 6. 相对贴近度 score dist_worst / (dist_best dist_worst) return score4.2 每一段代码在干什么正向化处理里我用了取倒数的方式这是标准做法。取倒数前要确保数据不为零如果原始数据里有0或负数建议先平移处理给整列加上一个常数让最小值大于0再取倒数。归一化这行代码是整个函数中最关键的一行。arr ** 2对所有元素求平方sum(axis0)按列求和再开方得到每列的模长每个元素除以对应列的模长就是向量归一化。加权那里我用weights / weights.sum()做了归一化保证权重加起来等于1。这样做的好处是即使你传入的权重不是严格归一化的比例函数也能自动校正不容易算错。最后计算贴近度时dist_worst / (dist_best dist_worst)这个向量化写法代替了循环效率高而且代码短数据量几千行也能秒出结果。4.3 怎么确认代码没写错代码写完后别急着上真实数据先做一个简单的验证。把决策矩阵里的所有数值改成完全相同理想情况下所有方案的得分应该都是0.5。因为大家到正理想解和负理想解的距离完全一样贴近度必然是0.5。这个验证通过说明公式逻辑没有硬伤。再拿一个两方案两指标的小例子手算一遍和代码输出对照。比如两个指标都是正向的方案1是(1, 2)方案2是(2, 1)手动算一次向量归一化和距离再对一下代码结果一致就没问题了。5. 实战案例5家供应商选优的完整跑通5.1 案例背景与数据输入拿前面那份供应商数据跑完整流程。业务背景是某制造企业要从A、B、C、D、E五家供应商里选一家作为核心合作伙伴6个指标分别是质量合格率、价格、交货准时率、售后响应时间、技术能力评分、产能。权重先按等权假设impacts参数和数据一一对应。score topsis(df.iloc[:, 1:].values, impactsimpacts) df[得分] score df[排名] score.rank(ascendingFalse).astype(int) print(df.sort_values(排名))5.2 运行结果与解读跑出来的结果会看到C供应商排在第一E排在第二D相对靠后。分析一下原因C在质量合格率、交货准时率、技术能力和产能几个正向指标上都表现突出价格虽然偏高但其他维度的优势拉高了整体贴近度。D的价格虽然有优势但产能、技术能力明显偏弱拖累了综合排名。这里要特别强调Topsis排名不是简单的加权求和排名。它是通过距离关系综合计算出来的某个指标特别突出或特别差都会在欧氏距离中被放大。所以你会看到价格很有竞争力的D并没有拿到高位这在现实中也很常见——单一优势撑不起综合竞争力。5.3 输出排序如何用于实际决策得到排序后别急着下结论。如果第一名的贴近度是0.82第二名是0.79两者差距很小说明前两名的综合表现接近此时可以继续细化权重、补充指标再做一轮评价如果第一名远超其他方案决策就相对明确。另外贴近度本身还能反映差距的“量级”。贴近度0.95和0.85虽然只差0.1但在距离空间里差距可能相当大。建议做决策时把得分转化为“梯度”而不是只盯着名次看。6. 常见坑与进阶组合熵权法确定权重、敏感性验证6.1 漏处理一个负向指标的排错链路我在刚开始用Topsis时栽过一次跟头症状很典型明明某供应商价格优势明显但排名反而靠后。排查了一遍才发现impacts里写错了方向把价格当成了正向指标。这类问题有一个快速的排查链路先输出归一化后的矩阵再把正理想解打印出来人工核对每个指标的理想值是否符合预期。如果价格列的理想解是数据集里的最高价说明方向处理有问题。另一个容易忽略的点是正向化取倒数之后再归一化列的方向是反过来的数值比例关系所以如果想让结果更直观取倒数前给数据加一个正数平移也可以。总之方向标签和预处理逻辑一定要一致性检查我一贯的做法是单独写一段注释标注每个指标的方向不依赖记忆。6.2 权重不靠拍脑袋熵权法Topsis组合等权假设只适合快速验证正式分析中我更推荐用熵权法先算权重再把权重喂给Topsis。熵权法的思路是某个指标的数据差异越大包含的信息量越大权重应该越高。这种客观赋值方式不需要专家判断和Topsis搭档很默契。def entropy_weight(data): arr np.array(data, dtypefloat) min_val arr.min(axis0) max_val arr.max(axis0) norm (arr - min_val) / (max_val - min_val 1e-10) p norm / (norm.sum(axis0) 1e-10) k 1 / np.log(len(arr)) e -k * (p * np.log(p 1e-10)).sum(axis0) d 1 - e w d / d.sum() return w weights entropy_weight(df.iloc[:, 1:].values) score topsis(df.iloc[:, 1:].values, weightsweights, impactsimpacts)需要注意的是熵权法里用的是极差归一化而Topsis内部用向量归一化这两者不冲突。熵权法只是在这里负责生成权重Topsis仍然按标准流程归一化数据。另外熵权法有个特点某个指标数据差异极小的时候权重会很低接近0这时候要考虑该指标是否真的对决策有区分度如果没有可以直接删掉。6.3 灵敏度分析与适用边界最后聊一个很多教程不写但实际很实用的动作灵敏度分析。评价结果受权重影响很大而权重本身就带有一定主观性所以我会在得到排序后换几组不同的权重组合重新跑一遍观察排名是否发生剧烈变化。假如换了三组权重前三名的排序基本没变说明评价结论稳健可以放心写进报告如果排名大幅波动就要考虑权重设置是否合理或者某些指标是否存在高度相关、重复加权的问题。Topsis对指标相关性没有显式控制当两个指标高度相关时相当于给同一维度的信息加了两倍权重这会悄悄扭曲结果。遇到这种情况建议先用相关性分析筛选指标或者做主成分合并同类指标后再做评价。还有一个边界要提醒Topsis是相对评价适合方案之间横向比较不适合用单一方案的贴近度去判断“绝对好坏”。如果样本量特别小只有两三个方案这个方法的区分度会明显下降此时要谨慎下结论。我在实际项目中养成的习惯是先用一份几行的小数据集把方向处理、归一化、排序逻辑全部验证一遍再上真实数据。Topsis的优势不在于算法多深奥而在于它把“综合优劣”变成了一个可计算、可解释、可复现的数字。把这个流程跑顺之后无论是写论文还是做企业决策分析这套代码和数据模板都能直接复用。后续你还可以在这个基础上扩展把指标分组做分层Topsis或者把阈值分类和贴近度结合玩法很多但核心的这套数据准备思路和代码逻辑不会变。本文还有配套的精品资源点击获取
返回列表