评价类问题——TOPSIS
Introduction
TOPSIS(优劣解距离法)和AHP(层次分析法)一样是综合评价方法,用于解决评价类问题。
相对于TOPSIS,AHP有如下局限性:
- 决策层 n 不能太多,否则矩阵一致性较差(无法通过一致性检验)。
- 无法处理决策层数据已知问题,自行协调填写精确度不足,且数据利用率不足。
TOPSIS的核心思想为评分构造公式 x−minmax−min\frac{x-min}{max-min}max−minx−min ,多指标情况下为 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
(无前提,所有情况适用)
Solution 2
(前提:所有 x_{ij} 均为正数)
Case 2 : 中间型 -> 极大型
Case 3 : 区间型 -> 极大型
Step 2 : 标准化处理
标准化处理目的是消去量纲差异。已有正向化后的矩阵 X 如下:
对每一个元素除以其所在列平方和:
得到标准化矩阵 Z:
Step 3 : 加权处理
参考文章
AHP法:评价类算法:层次分析法笔记(附Python代码)层次分析法python代码张张在努力_Lambda的博客-CSDN博客
若评价指标无重要性之分,则可直接跳转Step 4
。以下使用熵权法进行加权。
首先保证矩阵中不存在负数(否则可以在进行一次归一化);
接着计算熵值:
其中 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 个对象与最大值和最小值距离,可以理解为多维距离公式:
第 i 个对象未归一化的得分:
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")
转载自:https://juejin.cn/post/7217759646881611831