likes
comments
collection
share

评价类问题——TOPSIS

作者站长头像
站长
· 阅读数 6

Introduction

TOPSIS(优劣解距离法)和AHP(层次分析法)一样是综合评价方法,用于解决评价类问题。

相对于TOPSIS,AHP有如下局限性

  • 决策层 n 不能太多,否则矩阵一致性较差(无法通过一致性检验)。
  • 无法处理决策层数据已知问题,自行协调填写精确度不足,且数据利用率不足。

TOPSIS的核心思想为评分构造公式 x−minmax−min\frac{x-min}{max-min}maxminxmin ,多指标情况下为 dis(x,min)dis(x,max)+dis(x,min)\frac{dis(x,min)}{dis(x,max)+dis(x,min)}dis(x,max)+dis(x,min)dis(x,min) (优劣解距离法名称的由来)。

Theory

以下为TOPSIS的理论分析及实现步骤。

Step 1 : 统一指标类型

评价类指标分类如下表:

指标类型解释参数
极大型(效益型)数值越大越好
极小型(成本型)数值越小越好
中间型数值越靠近(中间)最优值越好x_{best}
区间型数值越靠近(中间)最优区间越好x_{left}、x_{right}

统一指标类型的常用方法为指标正向化,即讲所有指标转换为极大型

Case 1 : 极小型 -> 极大型

Solution 1 (无前提,所有情况适用)

xij  ⟹  maxj−xijx_{ij} \; {\Longrightarrow} \; max_j-x_{ij} xijmaxjxij

Solution 2前提:所有 x_{ij} 均为正数)

xij  ⟹  1xx_{ij} \; {\Longrightarrow} \; \frac{1}{x} xijx1

Case 2 : 中间型 -> 极大型

M=max{∣xij−xj_best∣}x~  ⟹  1−∣xij−xj_best∣MM=max\{|x_{ij}-x_{j\_best}|\} \\ \widetilde{x}\;{\Longrightarrow}\;1-\frac{|x_{ij}-x_{j\_best}|}{M} M=max{xijxj_best}x1Mxijxj_best

Case 3 : 区间型 -> 极大型

M=max{xleft−min{xj},max{xj}−xright}x  ⟹  {1−xleft−xMx<xleft1xleft≤x≤xright1−x−xrightMx>xrightM=max\{x_{left}-min\{x_j\},max\{x_j\}-x_{right}\} \\ x\;{\Longrightarrow}\; \begin{cases} 1-\frac{x_{left}-x}{M} & x<x_{left} \\ 1 & x_{left}\leq x\leq x_{right} \\ 1-\frac{x-x_{right}}{M} & x>x_{right} \\ \end{cases} M=max{xleftmin{xj},max{xj}xright}x1Mxleftx11Mxxrightx<xleftxleftxxrightx>xright

Step 2 : 标准化处理

标准化处理目的是消去量纲差异。已有正向化后的矩阵 X 如下:

X=[x11x12⋯x1mx21x22⋯x2m⋮⋮⋱⋮xn1xn2⋯xnm]X=\begin{bmatrix} x_{11} & x_{12} & \cdots & x_{1m} \\ x_{21} & x_{22} & \cdots & x_{2m} \\ \vdots & \vdots & \ddots & \vdots \\ x_{n1} & x_{n2} & \cdots & x_{nm} \\ \end{bmatrix} X=x11x21xn1x12x22xn2x1mx2mxnm

对每一个元素除以其所在列平方和:

Zij=xij∑i=1nxij2Z_{ij}=\frac{x_{ij}}{\sqrt{\sum_{i=1}^{n} x_{ij}^2}} Zij=i=1nxij2xij

得到标准化矩阵 Z:

Z=[z11z12⋯z1mz21z22⋯z2m⋮⋮⋱⋮zn1zn2⋯znm]Z=\begin{bmatrix} z_{11} & z_{12} & \cdots & z_{1m} \\ z_{21} & z_{22} & \cdots & z_{2m} \\ \vdots & \vdots & \ddots & \vdots \\ z_{n1} & z_{n2} & \cdots & z_{nm} \\ \end{bmatrix} Z=z11z21zn1z12z22zn2z1mz2mznm

Step 3 : 加权处理

参考文章

AHP法:评价类算法:层次分析法笔记(附Python代码)层次分析法python代码张张在努力_Lambda的博客-CSDN博客

熵权法:评价类算法:熵权法笔记(附Python代码)熵权法python代码张张在努力_Lambda的博客-CSDN博客

若评价指标无重要性之分,则可直接跳转Step 4。以下使用熵权法进行加权。

首先保证矩阵中不存在负数(否则可以在进行一次归一化);

接着计算熵值:

e=−1ln⁡(n)×∑i=1npi×ln⁡(pi)e=-\frac{1}{\ln(n)} \times \sum_{i=1}^{n} p_i \times \ln(p_i) e=ln(n)1×i=1npi×ln(pi)

其中 p 值为相应值除以一列中数值的和;

最后构建权重,用 1-e 得到信息的效用值,再对权重进行归一化即可得到权重 w ,将权重矩阵与数据矩阵相乘获得加权后的数据矩阵。

Step 4 : 计算得分

最大值向量 Z+=[Z1+,Z2+,...,Zm+]Z^+=[Z_1^+,Z_2^+,...,Z_m^+]Z+=[Z1+,Z2+,...,Zm+]

最小值向量 Z−=[Z1−,Z2−,...,Zm−]Z^-=[Z_1^-,Z_2^-,...,Z_m^-]Z=[Z1,Z2,...,Zm]

计算第 i 个对象与最大值和最小值距离,可以理解为多维距离公式:

Di+=∑j=1m(Zj+−Zij)2Di−=∑j=1m(Zj−−Zij)2D_i^+=\sqrt{\sum_{j=1}^{m}(Z_j^+-Z_{ij})^2} \\ D_i^-=\sqrt{\sum_{j=1}^{m}(Z_j^--Z_{ij})^2} \\ Di+=j=1m(Zj+Zij)2Di=j=1m(ZjZij)2

第 i 个对象未归一化的得分:

Si=Di−Di++Di−S_i=\frac{D_i^-}{D_i^++D_i^-} Si=Di++DiDi

Step 5 : 归一化

矩阵全局归一化。

Code

Python(只有py,鼠鼠不会Matlab /(ㄒoㄒ)/~~

 # -*- coding: utf-8 -*-
 # @File    :   topsis.py
 # @Time    :   2023/03/31 15:57:36
 # @Author  :   HzzzQ 
 # @College :   Computer Science & Engineering, CEIE, Tongji University
 ​
 import numpy as np
 ​
 ''' step 1  输入数据 '''
 ​
 ''' 样例数据:
 5000 0.01 7.35 89
 4500 0.2 7 63
 4000 0.1 7.42 201
 4400 0.0 7.10 60
 5100 0.03 7.52 180
 '''
 ​
 # 数据输入和类型划分
 print("请输入参评对象数目n:")
 n = eval(input())
 print()
 ​
 print("请输入评价指标数目m:")
 m = eval(input())
 print()
 ​
 print("请输入类型矩阵:1:极大型,2:极小型,3:中间型,4:区间型")
 kind = input().split(" ")
 print()
 ​
 print("请输入矩阵:")
 A = np.zeros(shape=(n, m))
 for i in range(n):
     A[i] = input().split(" ")
     A[i] = list(map(float, A[i]))
 print()
 print("输入矩阵为:\n{}".format(A))
 print()
 ​
 ​
 ''' step 2  统一指标类型 '''
 ​
 # 极小型指标转化为极大型指标:
 def minTomax(maxx, x):
     x = list(x)
     ans = [[(maxx-e)] for e in x]       # 可选择(极小型 -> 极大型)统一方式
     # ans = [list(1/e) for e in x]
     return np.array(ans)
 ​
 # 中间型指标转化为极大型指标:
 def midTomax(bestx, x):
     x = list(x)
     h = [abs(e-bestx) for e in x]
     M = max(h)
     if M == 0:
         M = 1
     ans = [[(1-e/M)] for e in h]
     return np.array(ans)
 ​
 # 区间型指标转化为极大型指标:
 def regTomax(lowx, highx, x):
     x = list(x)
     M = max(lowx-min(x), max(x)-highx)
     if M == 0:
         M = 1
     ans = []
     for i in range(len(x)):
         if x[i]<lowx:
             ans.append([(1-(lowx-x[i])/M)])
         elif x[i]>highx:
             ans.append([(1-(x[i]-highx)/M)])
         else:
             ans.append([1])
     return np.array(ans)
 ​
 X = np.zeros(shape=(n, 1))
 # 根据输入的每一列类型进入不同函数
 for i in range(m):
     if kind[i]=="1":
         v = np.array(A[:, i])
         
     elif kind[i]=="2":
         maxA = max(A[:, i])
         v = minTomax(maxA, A[:, i])
         
     elif kind[i]=="3":
         print("类型三:请输入最优值:")
         bestA = eval(input())
         v = midTomax(bestA, A[:, i])
         print()
         
     elif kind[i]=="4":
         print("类型四:请输入区间[a, b]值a:")
         lowA = eval(input())
         print()
         print("类型四:请输入区间[a, b]值b:")
         highA = eval(input())
         print()
         v = regTomax(lowA, highA, A[:, i])
         
     if i==0:
         X = v.reshape(-1, 1)
     else:
         X = np.hstack([X, v.reshape(-1, 1)])
         
 print("统一指标后矩阵为:\n{}".format(X),'\n')
 ​
 ​
 ''' step 3   标准化处理 '''
 ​
 X = X.astype('float')
 for j in range(m):
     X[:, j] = X[:, j]/np.sqrt(sum(X[:, j]**2))
 print("标准化矩阵为:\n{}".format(X),'\n')
 ​
 ​
 ''' step 4   获取权重指标 '''
 # 熵权法
 # 这里默认了矩阵中不存在负数;若存在负数,需要再次归一化
 p = X                                   # 计算概率矩阵P
 for j in range(m):
     p[:, j] = X[:, j]/sum(X[:, j])
 ​
 E = np.array(X[0, :])                   # 计算熵值
 for j in range(m):
     E[j] = -1/np.log(n)*sum(p[:, j]*np.log(p[:, j]+ 1e-5))
 ​
 w = (1-E)/sum(1-E)                      # 计算熵权
 print("权重矩阵为:\n{}".format(w),'\n')
 ​
 ​
 '''step 5   最大值最小值距离'''
 ​
 # 得到加权后的数据
 R = X*w
 print("权重后的数据:\n{}".format(R),'\n')
 # 得到最大值最小值距离
 ​
 r_max = np.max(R, axis=0)              # 每个指标的最大值
 r_min = np.min(R, axis=0)              # 每个指标的最小值
 d_z = np.sqrt(np.sum(np.square((R - np.tile(r_max, (n, 1)))), axis=1))      # D+向量
 d_f = np.sqrt(np.sum(np.square((R - np.tile(r_min, (n, 1)))), axis=1))      # D-向量
 print('每个指标的最大值:', r_max,'\n')
 print('每个指标的最小值:', r_min,'\n')
 print('d+向量:', d_z,'\n')
 print('d-向量:', d_f,'\n')
 ​
 ​
 ''' step 6   计算排名 '''
 ​
 s = d_f/(d_z+d_f)
 Score = 100*s/max(s)
 for i in range(len(Score)):
     print(f"第{i+1}个百分制得分为:{Score[i]}\n")
 ​