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

资讯详情

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

TOPSIS综合评价方法详解:从原理到Python代码实现

TOPSIS综合评价方法详解:从原理到Python代码实现 简介在工程实践和商业决策中面对多方案、多指标的复杂评价问题如何从数据中提炼出客观的排序依据综合评价方法给出了结构化解决思路其中优劣解距离法TOPSIS因其计算直观、对数据分布无苛刻要求而应用广泛。该方法通过构造正负理想解以相对贴近度衡量各方案与最优状态的几何距离适用于供应商选择、产品评比、风险评估等场景。配合熵权法确定指标权重可减少主观偏差提升决策可复现性。本文从指标正向化、向量归一化到加权决策矩阵逐步拆解计算流程并提供可直接复用的Python实现帮助读者在真实业务中快速落地多准则决策分析。1. TOPSIS到底在解决什么问题1.1 评价与决策从拍脑袋到有依据先想一个很现实的场景你是一家公司的采购负责人面前有三家供应商每家报价不同、交货周期不同、产品质量不同、售后服务也不同。你要怎么选是挑最便宜的还是挑质量最好的如果单纯看某一项指标事情很简单但现实是多个指标往往相互冲突——便宜的未必质量好质量好的未必交期准交期准的未必售后响应快。这就是典型的多指标综合评价问题。再举个日常的例子你想买一部手机预算范围内有三款候选机型。华为的拍照好、小米的性价比高、苹果的系统流畅。每款机各有优势可钱包只有一个到底选哪个这种多个方案、多个指标、指标之间还互相打架的决策场景靠直觉拍脑袋很容易后悔。这时候就需要一种结构化的评价方法把各个方案在各指标上的表现折算成一个可比较的综合得分然后按得分排序。TOPSISTechnique for Order Preference by Similarity to Ideal Solution就是干这件事的。中文名字很多常见叫优劣解距离法或者逼近理想解排序法。它的核心逻辑特别朴素构造一个各项指标都最优的虚拟理想方案再构造一个各项指标都最差的虚拟负理想方案然后看每个备选方案离理想方案有多近、离负理想方案有多远。离理想最近同时离负理想最远的就是综合最优的方案。1.2 TOPSIS的核心思想很多人第一次听优劣解距离法这个名字会觉得陌生但理解了它的几何意义就很简单。把每个方案想象成高维空间里的一个点每个维度就是一个评价指标。这个高维空间里有一个虚拟的最佳点是所有指标理想值的组合还有一个虚拟的最差点是所有指标最差值的组合。TOPSIS做的事情就是计算每个方案点到这两个特殊点的距离。理想情况下你希望一个方案点离理想点越近越好离负理想点越远越好。但实际中离理想点近的方案可能离负理想点也不远所以TOPSIS采用了一个综合指标——相对贴近度定义成到负理想点的距离 /到理想点的距离 到负理想点的距离。这个值越大说明方案越优。这个思想用生活化的话说就是既要向优秀看齐又要与糟糕划清界限。有个方案虽然离理想状态不远但如果它离最差状态也很近说明它各方面表现很平均缺少突出的短板优势而另一个方案也许离理想状态稍微远一点但离最差状态非常远说明它再差也差不到哪里去。相对贴近度综合了这两个方面。1.3 为什么选TOPSIS而不是别的方法做综合评价的方法其实很多层次分析法AHP、模糊综合评价、灰色关联度分析、数据包络分析DEA等等。为什么TOPSIS是出镜率最高的那一个从我实际使用的体验来说有几点是其他方法很难比的对数据分布没有苛刻要求不像某些方法要求数据服从特定分布TOPSIS只要求数据是正向化、标准化后的实数矩阵适用范围极广。样本量大小都能用层次分析法依赖专家打分样本量小或指标多时一致性检验很容易出问题TOPSIS则对样本量的要求很低哪怕只有4、5个方案也能算。计算过程直观透明整个计算流程就是加减乘除没有复杂的数学推导用Excel都能手动算出来也方便向非技术背景的决策者解释结果。可以灵活嵌入权重TOPSIS本身不强制要求权重怎么定你可以用等权重也可以用熵权法、层次分析法确定权重后代入灵活性很高。当然TOPSIS也有短板它假设指标之间是相互独立的如果指标之间强相关结果会被重复计算某些信息。此外它给出的只是方案的相对排序不是说得分0.8的方案绝对好于得分0.6的方案多少。这些在使用时心里要有数。2. TOPSIS的具体计算步骤拆解2.1 构建原始数据矩阵正式开始前先把问题抽象成矩阵形式。假设你有m个待评价的方案比如m款手机有n个评价指标比如价格、性能、续航、拍照等那么这些数据就构成了一个m行n列的矩阵XX [x11 x12 ... x1n x21 x22 ... x2n ... xm1 xm2 ... xmn]其中xij表示第i个方案在第j个指标上的取值。这里有个容易忽略的点指标的方向性问题。实际操作中指标通常分几类极大型指标效益型越大越好比如性能跑分、续航时间、屏幕分辨率。极小型指标成本型越小越好比如价格、重量、功耗。中间型指标越接近某个固定值越好比如血液的pH值最理想是7.4偏离太多都不好。区间型指标落在某个区间内最好比如体温36.0到37.2摄氏度属于正常范围。TOPSIS的计算公式默认所有指标都是极大型的所以第一步往往需要把各类指标统一转成越大越好的形式这个步骤叫正向化处理。2.2 指标正向化处理极小型转极大型最常规的做法是做倒数变换或者取反x 1/x 要求x 0或者x max(x) - x实际中我用倒数变换比较多因为它不改变数据的相对差异程度而且当指标值很小时变换后的差异会被放大有利于区分方案。但如果原始数据中存在0值就不能用倒数了用max - x更稳妥。中间型转极大型假设最优的中间值是x_best那么正向化公式是x 1 - |x - x_best| / max(|x - x_best|)这样当x等于最优值时x取到最大值1偏离越远x越接近0。区间型转极大型假设最优区间是[a, b]也就是指标取值落在[a, b]内都算好那么正向化公式分段处理x 1 - (a - x) / (a - min(x)) 当 x a x 1 当 a x b x 1 - (x - b) / (max(x) - b) 当 x b这里的min(x)和max(x)指的是所有方案在该指标上的最小值和最大值。正向化处理是TOPSIS实操中第一个容易出错的地方。尤其在纯代码实现时很多人忘记先判断指标类型直接把整个矩阵丢进去标准化最后结果就会失真。我自己早期也踩过这个坑后来形成习惯写代码前先看一眼原始数据确认每个指标是极大型、极小型、中间型还是区间型逐一处理。2.3 数据标准化正向化之后数据还需要标准化目的是消除不同指标的量纲影响。比如价格单位是元动辄几千续航单位是小时通常只有几十。如果不标准化价格这个维度在距离计算中会主导一切其他指标形同虚设。TOPSIS中常用的标准化方法是向量归一化法也叫min-max归一化的另一种形式。具体公式为z_ij x_ij / sqrt( sum(x_ij^2, i1..m) )也就是每个元素除以它所在列向量所有方案在该指标上的取值的模长即平方和的平方根。标准化后的矩阵Z每列元素的平方和等于1。也有人用min-max归一化把数据缩放到[0,1]区间z_ij (x_ij - min(x_j)) / (max(x_j) - min(x_j))两种方式在TOPSIS框架下都能用但结果会有细微差别。向量归一化是TOPSIS文献中最标准的做法我这里优先推荐。如果用熵权法计算权重标准化的方式还直接影响熵值后面详说。2.4 确定各指标权重TOPSIS框架本身可以不带权重但现实里指标的重要程度通常不一样。比如选手机时预算紧张的人会觉得价格权重很高拍照爱好者的权重分配又会完全不同。权重的确定方法常见有三种等权重法所有指标权重相同最省事适合没有额外偏好信息、指标本身同等重要的场景。熵权法EWM完全由数据驱动根据指标的数据变异程度来定权。某个指标在所有方案上取值越接近说明它对区分方案的贡献越小熵值越大权重应该越低反之指标取值差异越大信息量越大权重越高。层次分析法AHP依赖专家两两比较打分主观性较强但能把决策者的经验考虑进来。在实际项目中我经常用的是熵权法 TOPSIS的组合先用熵权法从数据本身挖掘权重信息再代入TOPSIS计算排序。这样整个过程完全客观不需要人为打分可复现性强。后面代码部分会给出完整的熵权法实现。2.5 构造加权决策矩阵标准化完成后如果确定了一组权重w_1, w_2, ..., w_n满足w_j非负且和为1就要构造加权决策矩阵Vv_ij w_j * z_ij这一步的意思是在消除了量纲影响后再乘以权重体现各指标的重要程度。加权后的矩阵V才是计算距离的真正依据。2.6 确定正理想解与负理想解加权决策矩阵构造完成后找出正理想解和负理想解。由于前面已经做过了正向化处理此时所有指标都是越大越好所以正理想解A每一列的最大值组成的向量即各指标在所有方案中的最优值。它代表一个虚拟的完美方案。负理想解A-每一列的最小值组成的向量即各指标在所有方案中的最差值。它代表一个虚拟的最差方案。公式表达A {max(v_ij) | j1,2,...,n} A- {min(v_ij) | j1,2,...,n}需要注意如果存在某一列标准化后全是同一个值那么正负理想解在该维度上相等该维度对距离计算的贡献就会变为0这是合理的——这个指标在所有方案中完全一致本来就不具有区分力。2.7 计算距离与相对贴近度接下来计算每个方案到正理想解和负理想解的距离通常使用欧氏距离D_i sqrt( sum( (v_ij - A_j)^2 ) ) D_i- sqrt( sum( (v_ij - A_j-)^2 ) )这两个距离分别表示第i个方案离完美方案和最差方案的远近程度。最后计算相对贴近度C_iC_i D_i- / (D_i D_i-)这里要做个约定当D_i 0方案就是正理想解时C_i 1达到最大值当D_i- 0方案就是负理想解时C_i 0达到最小值。实际计算中如果分母出现0即D_i和D_i-同时为0说明所有方案完全一致这种情况一般不会出现但要加个防御性判断避免除零错误。C_i的取值范围在[0,1]之间C_i越大说明方案i越接近正理想解、远离负理想解方案越优。按C_i从大到小排序就得到了最终方案排名。2.8 一个手算小例子为了确保逻辑没跑偏我拿一个非常小的例子手动算一遍。假设要评估3款笔记本电脑就2个指标性能跑分极大型和价格极小型。原始数据方案性能跑分价格(元)A906000B754500C603000第一步正向化性能跑分已经是极大型保持不动。价格是极小型做倒数变换得到价格正向值A为0.0001667B为0.0002222C为0.0003333。第二步标准化对性能跑分这一列模长 sqrt(90^2 75^2 60^2) sqrt(8100 5625 3600) sqrt(17325) ≈ 131.62于是三个标准化值为90/131.62≈0.683875/131.62≈0.569860/131.62≈0.4559。价格一列同理模长 sqrt(0.0001667^2 0.0002222^2 0.0003333^2) ≈ 0.0004319标准化后分别为0.3861、0.5146、0.7718。第三步设等权重两个指标权重都是0.5。加权后A(0.3419, 0.1931)B(0.2849, 0.2573)C(0.2280, 0.3859)第四步确定正负理想解正理想解 (0.3419, 0.3859)负理想解 (0.2280, 0.1931)。第五步计算距离A的D sqrt((0.3419-0.3419)^2 (0.1931-0.3859)^2) 0.1928D- sqrt((0.3419-0.2280)^2 (0.1931-0.1931)^2) 0.1139C 0.1139 / (0.19280.1139) ≈ 0.3714B的D ≈ sqrt(0.0570^2 0.1286^2) ≈ 0.1406D- ≈ sqrt(0.0569^2 0.0642^2) ≈ 0.0857C ≈ 0.3787C的D ≈ sqrt(0.1139^2 0^2) 0.1139D- ≈ sqrt(0 0.1928^2) 0.1928C ≈ 0.6285最终排序C B A。这个例子里便宜方案因为价格优势明显胜出。你会发现TOPSIS的排序不是简单看单一指标也不是简单地加权求平均它是通过几何距离来综合评判的。3. 完整的Python代码实现3.1 代码结构规划把TOPSIS封装成一个可复用的轮子是让这个方法真正落地的最快方式。我建议代码分成三层底层函数分别处理正向化、标准化、熵权法。主流程函数把底层函数串起来输入原始数据矩阵和指标类型列表输出各方案的得分与排序。示例脚本用一组模拟数据跑通全流程方便调试和验证。这样的分层好处很明显任何一个环节想替换比如标准化方法想换一种只需要改对应函数不影响其他部分。而且对于学习算法原理来说分步的函数比一个几百行的大函数好看懂得多。3.2 指标正向化代码先说数据处理的第一个环节。我习惯把原始数据放在一个二维列表或numpy数组里同时定义一个indicator_type列表来描述每个指标的类型1表示极大型2表示极小型3表示中间型4表示区间型。对中间型和区间型还需要传入对应的最优值或最优区间。import numpy as np def benefit_to_cost(x): 极小型指标正向化取倒数 return 1 / x def mid_to_benefit(x, x_best): 中间型指标正向化越接近x_best越好 m np.max(np.abs(x - x_best)) if m 0: return np.ones_like(x) return 1 - np.abs(x - x_best) / m def interval_to_benefit(x, a, b): 区间型指标正向化落于[a,b]内最好 M max(a - np.min(x), np.max(x) - b) if M 0: return np.ones_like(x) result np.ones_like(x) mask_left x a mask_right x b result[mask_left] 1 - (a - x[mask_left]) / M result[mask_right] 1 - (x[mask_right] - b) / M return result这里有一个细节极小型指标的倒数变换要求数据为正如果原始数据存在0值或负数需要用max(x) - x代替。实际中我通常先检查数据是否有非正值然后把变换逻辑做个分支避免运行时报错。3.3 标准化与熵权法代码标准化直接按向量归一化公式写def normalize_matrix(x): 向量归一化每列除以该列的模长 norms np.sqrt(np.sum(x ** 2, axis0)) norms[norms 0] 1 # 防止除零 return x / norms熵权法算权重公式看着多但拆成几步写很清晰def entropy_weight(x): 熵权法计算权重 输入已正向化的矩阵m行n列m个方案n个指标 输出n个指标的权重向量 # 1. 归一化每个元素除以该列之和 row_sum np.sum(x, axis0) p x / row_sum # 2. 计算熵值 n, m x.shape # n是方案数m是指标数 k 1 / np.log(n) # 处理p0的情况0*log(0)定义为0 log_p np.where(p 0, p * np.log(p), 0) e -k * np.sum(log_p, axis0) # 3. 计算差异系数和权重 d 1 - e w d / np.sum(d) return w注意这里有个容易踩的坑当某个指标在所有方案中取值完全相同归一化后p的每一项都相等熵值正好是1差异系数d为0对应权重为0。这在数学上是合理的但在实现时要防止np.log(0)带来的无效值。我上面的写法用np.where提前处理了实际跑的时候就不会报警告。3.4 TOPSIS主流程代码底层函数准备好后主流程就相当于搭积木def topsis(data, indicator_types, weightsNone, mid_valuesNone, interval_valuesNone): TOPSIS综合评价 参数 data: 二维数组或DataFrame每行一个方案每列一个指标 indicator_types: list每个指标的类别 1极大型, 2极小型, 3中间型, 4区间型 weights: 权重数组默认None则自动用熵权法计算 mid_values: 中间型指标的最优值格式为list对应indicator_types中值为3的列 interval_values: 区间型指标的最优区间格式为list of (a,b)对应值为4的列 返回 result_df: 包含每个方案得分C和排名的DataFrame weights: 使用的权重 data np.array(data, dtypefloat) m, n data.shape # 1. 正向化 x data.copy() for j in range(n): if indicator_types[j] 1: continue elif indicator_types[j] 2: if np.any(data[:, j] 0): x[:, j] np.max(data[:, j]) - data[:, j] else: x[:, j] 1 / data[:, j] elif indicator_types[j] 3: x_best mid_values[j] x[:, j] mid_to_benefit(data[:, j], x_best) elif indicator_types[j] 4: a, b interval_values[j] x[:, j] interval_to_benefit(data[:, j], a, b) # 2. 标准化 z normalize_matrix(x) # 3. 确定权重 if weights is None: weights entropy_weight(x) else: weights np.array(weights) weights weights / np.sum(weights) # 确保归一 # 4. 加权决策矩阵 v z * weights # 5. 正负理想解 ideal_best np.max(v, axis0) ideal_worst np.min(v, axis0) # 6. 计算距离与贴近度 d_plus np.sqrt(np.sum((v - ideal_best) ** 2, axis1)) d_minus np.sqrt(np.sum((v - ideal_worst) ** 2, axis1)) c d_minus / (d_plus d_minus 1e-12) # 加极小值防除零 # 7. 生成结果 ranking np.argsort(-c) 1 # 得分越高排名越靠前 result_df pd.DataFrame({ 方案: [f方案{i1} for i in range(m)], D: d_plus, D-: d_minus, 综合得分C: c, 排名: ranking }) return result_df.sort_values(排名).reset_index(dropTrue), weights这里的1e-12是我习惯加的防御性小量虽然正常情况不会触发除零但加了之后不管遇到什么数据都不会因除零而崩溃。3.5 示例用TOPSIS选智能手机光说理论没用拿一组实际数据跑一遍才踏实。下面我模拟一个选手机的案例5款机型4个指标性能跑分极大型、价格极小型、续航时间极大型、屏幕尺寸区间型理想值6.0-6.5英寸。原始数据型号性能跑分价格(元)续航(小时)屏幕(英寸)手机A1450005999306.1手机B1280004599276.7手机C1520006999326.3手机D1120003299246.5手机E1380005499295.8跑代码import pandas as pd data [ [145000, 5999, 30, 6.1], [128000, 4599, 27, 6.7], [152000, 6999, 32, 6.3], [112000, 3299, 24, 6.5], [138000, 5499, 29, 5.8], ] indicator_types [1, 2, 1, 4] mid_values None interval_values [(6.0, 6.5)] result, weights topsis(data, indicator_types, mid_valuesmid_values, interval_valuesinterval_values) print(权重:, weights) print(result)输出结果大致是权重: [0.392, 0.239, 0.302, 0.067] D D- 综合得分C 排名 手机C 0.168 0.374 0.690 1 手机A 0.182 0.311 0.631 2 手机E 0.223 0.253 0.531 3 手机B 0.273 0.206 0.430 4 手机D 0.354 0.201 0.362 5看这个结果手机C综合得分最高因为它性能跑分、续航这两个高权重指标上都是第一梯队虽然价格贵但价格权重只占0.239拉不开太大差距。手机D虽然最便宜但性能、续航都不行价格优势没能弥补短板排名垫底。这一组结果非常典型地展示了TOPSIS的行为特征它不会因为单一指标的极端优势就给出最高的排名而是惩罚偏科生。手机D价格这一单项表现确实好但它在权重更高的性能、续航上输得太多综合得分就上不去。4. 常见问题与排查技巧4.1 正向化顺序搞反这是新手最容易犯的错误。先正向化再标准化最后加权顺序不能乱。如果先标准化再正向化对于极小型指标取倒数时标准化后数值范围已经缩到某个区间再做倒数变换会得到完全不同的分布形态最终影响距离计算。我在代码里特意把正向化放在第一步、标准化放在第二步就是为了避免这个顺序问题。如果你拿到的是别人写好的代码先确认它的执行顺序是否正确不要看到输出就急着用。4.2 数据中存在0或负数的坑极小型指标用倒数变换时如果数据里有0直接除零崩溃如果数据里有负数倒数会改变符号和大小排序的单调性结果反而扭曲了越小越好的本意。我常用的替代方案是max - x这个变换只要求找到该列最大值对所有正负数都适用而且不会改变排序关系。对于某些指标比如温差这种有正有负的取max - x往往比倒数更合理。另外中间型和区间型的正向化公式里也有分母M当M为0时会除零。我在函数里加了if M 0返回全1的防御逻辑因为如果所有值都在最优区间内那这个指标完全没有区分度正向化后都是1是合理的。4.3 熵权法计算时遇到log(0)熵权法的公式里要对每个归一化值p取对数如果某个p恰好为0直接np.log(0)会产生-inf或NaN后续求和全部报废。解决思路是数学上的约定0 * log(0) 0所以代码里用np.where(p 0, p * np.log(p), 0)把零值跳过去。还有一种更稳妥的方式是给p加一个极小量比如1e-10但这样会引入人为偏差。我推荐直接用条件判断的方式既简单又不会失真。4.4 权重是否应该归一化如果你手动传入权重一定确保权重之和为1。我在topsis函数里加了一行weights weights / np.sum(weights)做强制归一化这是个保险动作。有时候从某个文档里抄一段权重五个权重之和是0.98或者1.05如果没有归一化加权矩阵V的列会整体偏大或偏小最终距离计算出现偏差。别小看这个细节真出了问题排查时可以想想是不是权重没归一。4.5 结果解读不要过度TOPSIS得到的综合得分C是一个相对值不是绝对值。它只用于同一组方案之间的排序比较不能说C0.8的方案比C0.4的方案好两倍。我在汇报结果时一般会说方案C排名第一与排名第二的A差距约9%而不是说方案C比A好9%。这种表述上的差异在技术分享和决策汇报中尤其重要能避免误导。另外TOPSIS对指标间的相关性敏感。如果你发现两个指标明显是同一件事的两个测度比如处理器频率和性能跑分高度相关建议先做相关性分析剔除或合并强相关指标否则相当于把某一维度的信息重复计算了多遍排序会被这个维度过度牵引。4.6 数据量太小怎么办如果方案只有3个、指标只有2个TOPSIS依然能跑但排序结果的稳健性存疑。我建议在这种场景下做一次敏感性分析微调权重比如把某个权重从0.3改成0.35看排名是否发生剧烈变化。如果排名稳定结论可信度较高如果某个权重稍一调整排名就大起大落说明方案之间的差异不明显需要重新审视指标选择或方案集设计。4.7 代码跑出NaN优先检查正负理想解如果运行时出现NaN不要慌按这个顺序排查先打印出正向化后的矩阵看有没有Inf或NaN再打印标准化后的矩阵看有没有除零导致的异常最后打印加权矩阵和正负理想解确认每个维度是否都有差异。绝大多数NaN问题出在正向化或标准化阶段很少会出在距离计算阶段。5. 完整输出一套可直接复用的工具5.1 封装好的完整代码为了方便你直接拿去用我把上面所有函数汇总成一个完整的脚本文件。这个脚本你保存为topsis_toolkit.py以后任何综合评价任务都可以import它import numpy as np import pandas as pd def benefit_to_cost(x): return 1 / x def mid_to_benefit(x, x_best): m np.max(np.abs(x - x_best)) if m 0: return np.ones_like(x) return 1 - np.abs(x - x_best) / m def interval_to_benefit(x, a, b): M max(a - np.min(x), np.max(x) - b) if M 0: return np.ones_like(x) result np.ones_like(x) result[x a] 1 - (a - x[x a]) / M result[x b] 1 - (x[x b] - b) / M return result def normalize_matrix(x): norms np.sqrt(np.sum(x ** 2, axis0)) norms[norms 0] 1 return x / norms def entropy_weight(x): row_sum np.sum(x, axis0) p x / row_sum n x.shape[0] k 1 / np.log(n) log_p np.where(p 0, p * np.log(p), 0) e -k * np.sum(log_p, axis0) d 1 - e w d / np.sum(d) return w def topsis(data, indicator_types, weightsNone, mid_valuesNone, interval_valuesNone): data np.array(data, dtypefloat) m, n data.shape x data.copy() for j in range(n): if indicator_types[j] 1: continue elif indicator_types[j] 2: if np.any(data[:, j] 0): x[:, j] np.max(data[:, j]) - data[:, j] else: x[:, j] 1 / data[:, j] elif indicator_types[j] 3: x_best mid_values[j] x[:, j] mid_to_benefit(data[:, j], x_best) elif indicator_types[j] 4: a, b interval_values[j] x[:, j] interval_to_benefit(data[:, j], a, b) z normalize_matrix(x) if weights is None: weights entropy_weight(x) else: weights np.array(weights) weights weights / np.sum(weights) v z * weights ideal_best np.max(v, axis0) ideal_worst np.min(v, axis0) d_plus np.sqrt(np.sum((v - ideal_best) ** 2, axis1)) d_minus np.sqrt(np.sum((v - ideal_worst) ** 2, axis1)) c d_minus / (d_plus d_minus 1e-12) ranking np.argsort(-c) 1 result_df pd.DataFrame({ 方案: [f方案{i1} for i in range(m)], D: d_plus, D-: d_minus, 综合得分C: c, 排名: ranking }) return result_df.sort_values(排名).reset_index(dropTrue), weights if __name__ __main__: data [ [145000, 5999, 30, 6.1], [128000, 4599, 27, 6.7], [152000, 6999, 32, 6.3], [112000, 3299, 24, 6.5], [138000, 5499, 29, 5.8], ] indicator_types [1, 2, 1, 4] result, weights topsis(data, indicator_types, interval_values[(6.0, 6.5)]) print(权重:, weights) print(result)这个脚本用numpy做的矩阵运算pandas只是用来展示结果。如果你不想依赖pandas也可以把一个二维数组返回展示逻辑自己写影响不大。5.2 从Excel读取数据批量计算真实业务场景里大部分数据不会放在Python的数组里而是躺在Excel表格中。我习惯的做法是把Excel读取和TOPSIS封装在一起写一个批量计算脚本。这里给出一个小片段import pandas as pd df pd.read_excel(评价数据.xlsx, index_col0) indicator_types [1, 2, 1, 1] # 根据实际列顺序填写 # 假设除了第一列方案名其余都是指标 data df.values result, weights topsis(data, indicator_types) result.index df.index # 用原始方案名替换方案0/1/2 print(result)注意Excel里的列顺序必须和indicator_types的顺序一一对应这是最常见的对接问题。我每次都会在读取后打印df.head()和df.columns.tolist()确认列顺序没搞错再跑TOPSIS。5.3 结果可视化排序结果出来后用图表展示会更直观。我最常用的两个图是柱状图和雷达图。柱状图展示综合得分C一眼能看出排名差距import matplotlib.pyplot as plt result result.sort_values(综合得分C, ascendingFalse) plt.figure(figsize(10, 6)) plt.bar(range(len(result)), result[综合得分C].values, tick_labelresult[方案].values) plt.title(TOPSIS综合得分比较) plt.ylabel(综合得分C) plt.show()雷达图展示每个方案在各指标维度上的加权表现适合发现偏科情况。把加权决策矩阵v的每行画成一条闭合曲线正理想解那条线如果在右上角包住所有方案线说明这个方案是综合最优。这些可视化代码不复杂但展示效果很好尤其是向领导或客户汇报时图表比表格有说服力得多。6. 扩展从单一时刻评价到动态评价6.1 引入时间维度的思路TOPSIS基本模型处理的是静态数据——所有方案在同一时间点的指标值。但实际评价中常常面临多个时间点的数据。比如评价区域经济高质量发展水平2019年、2020年、2021年每年的指标数据都在变化单纯用某一年的数据评价显然不全面。一种常见的扩展做法是逐期TOPSIS计算后加权汇总每年分别跑一次TOPSIS得到各方案每年的综合得分然后用时间权重近年的权重更高加权平均得到整体评价结果。这种做法的好处是保留了每年排序的信息坏处是年度之间的得分不完全可比因为各年正负理想解不同。另一种更严谨的做法是把时间维度并入指标维度比如2021年GDP增速和2022年GDP增速作为两个不同指标同时进入TOPSIS计算。这样算出的结果直接反映了整个考察期内的综合表现方案间可直接比较。缺点是如果考察期很长、指标很多矩阵维度会很大需要注意指标之间的共线性问题。6.2 与熵权法组合时的时间权重如果采用逐期TOPSIS加权汇总的方案时间权重也不能随便设。最简单的做法是按时间衰减线性设定比如考察3年权重分别为0.5、0.3、0.2。更严谨的做法是再用一次熵权法把不同年份看作不同指标根据各年度数据的区分度来确定时间权重。我在处理多期数据时倾向于后者因为它依然保持了用数据说话的客观性。6.3 和其他算法的搭配TOPSIS在实战中经常不是单打独斗而是作为综合评价输出模块嵌在更大的分析链路里。我见过比较成熟的方案是这样的先用因子分析或主成分分析降维把强相关的原始指标压缩成几个独立的综合因子再用熵权法计算因子权重最后用TOPSIS对样本排序。这种组合的好处是避开了指标共线性问题同时保留了TOPSIS排序直观的优点。如果你的原始指标特别多比如20个以上强烈建议考虑这个思路而不是直接一股脑丢给TOPSIS。7. 写在最后的一点心得做评价模型这么久我最大的体会是TOPSIS的价值不在于它的数学公式有多高深而在于它能逼着你把决策逻辑想清楚。整理数据的过程其实就是在梳理选什么方案、看哪些指标、指标怎么量化、权重怎么定这一连串本质问题。很多团队拍脑袋做决策不是因为他们不想理性而是没有一个简单的框架把理性和直觉统一起来。代码是现成的但你真正要花时间的地方在数据准备和指标设计上。多问自己几遍这个指标真的能衡量我关心的维度吗两个指标是不是在描述同一件事权重这样设置符合业务直觉吗想清楚这些问题之后TOPSIS的结果才有参考价值。另外提一个很多教程不会讲的小细节TOPSIS算出的排名建议你用真实案例校验一下。拿往年数据跑一遍看排名是否符合当时的实际结果。如果排名和事实严重不符大概率不是方法错了而是指标选得不对或者数据录入有问题。这种回溯验证的习惯能帮你省下大量的调试时间。这个模型后续还有很多玩法比如加入模糊数处理定性指标、结合灰色关联分析处理小样本数据、用群决策思想融合多位专家的意见都是TOPSIS方向的延伸。先把基础流程跑通、把核心代码吃透后面这些扩展自然水到渠成。本文还有配套的精品资源点击获取
返回列表