TOPSIS法的原理及其 Python 实现
TOPSIS法的原理
TOPSIS (Technique for Order Preference by Similarity to an Ideal Solution )法是C.L.Hwang和K.Yoon于1981年首次提出,TOPSIS法根据有限个评价对象与理想化目标的接近程度进行排序的方法,是在现有的对象中进行相对优劣的评价。TOPSIS法是一种逼近于理想解的排序法,该方法只要求各效用函数具有单调递增(或递减)性就行。TOPSIS法是多目标决策分析中一种常用的有效方法,又称为优劣解距离法。
基本思路就是通过假定正负理想解,测算各个样本与正负理想解的距离,得到其与理想方案的相对贴进度(即距离正理想解越近的同时距离负理想解越远),进行各个评价对象的右列排序。
TOPSIS法的思路
数据的处理
这部分我们可以结合《熵权法原理及其 Python 实现》 来进行处理,计算得到权重 w
以及标准化 p
,然后我们可以获得我们的 Z
矩阵如下:
$$ Z = (z_{ij})_{n \times m} = (p_{ij}*w_j) $$
确定正负理想解
正理想解指各指标都达到样本中最好的值,负理想解指各指标都为样本中最差的值。
$$ Z^{+} = (\max\{z_{11}, z_{21}, ... , z_{n1}\}, \max\{z_{12}, z_{22},...,z_{n2}\}, ..., \max\{z_{1m}, z_{2m}, ..., z_{mn}\}) = (Z^{+}_1, Z^{+}_2, ..., Z^{+}_m) $$
$$ Z^{-} = (\min\{z_{11}, z_{21}, ... , z_{n1}\}, \min\{z_{12}, z_{22},...,z_{n2}\}, ..., \min\{z_{1m}, z_{2m}, ..., z_{mn}\}) = (Z^{-}_1, Z^{-}_2, ..., Z^{-}_m) $$
计算正负理想解距离
$$ D^{+}_i = \sqrt{\displaystyle\sum^m_{j = 1}(z_{ij} - z^{+}_j)^2} $$
$$ D^{-}_i = \sqrt{\displaystyle\sum^m_{j = 1}(z_{ij} - z^{-}_j)^2} $$
计算贴合程度
$$ C_i = \frac{D^{-}_i}{D^{+}_i + D^{-}_i} $$
其中 Ci
取值范围在 [0, 1] ,从上述式子可观察出,若 Ci
越靠近 1 ,即表明样本评分越好。
Python 实现
#!/usr/bin/env python
# coding=utf-8
import numpy as np
def negativeIndex(data):
n, m = data.shape
x = np.ones([n, m])
for j in range(m):
max_xj, min_xj = max(data.T[j]), min(data.T[j])
for i in range(n):
x[i, j] = (data[i, j] - min_xj) / (max_xj - min_xj)
return x
def ln(x):
return np.log(x)
def calcP(data):
n, m = data.shape
p = np.ones([n, m])
for j in range(m):
for i in range(n):
p[i, j] = (data[i, j] / np.sum(data.T[j])).astype(np.float64)
return p
def calcEntropy(data):
# data = calcP(data)
# print("Calculated P:")
# print(data)
n, m = data.shape
k = 1.0 / ln(n)
E = np.ones(m)
for j in range(m):
sum = 0
for i in range(n):
sum += (data[i, j] * np.nan_to_num(ln(data[i, j])))
print("Sum = ", sum)
E[j] = -k * sum
return E
def calcWeight(data):
data = calcEntropy(data)
return (1 - data) / (1 - data).sum()
def calcWeight2(data):
E = np.nansum(-data * np.log(data) / np.log(len(data)), axis=0)
print(np.nansum(data * np.log(data), axis=0))
return (1 - E) / (1 - E).sum()
def topsis(data, weight=None):
n, m = data.shape
weight = calcWeight(data)
Zs = np.ones(data.shape)
Z_Plus, Z_Minus = np.ones(m), np.ones(m)
for j in range(m):
for i in range(n):
Zs[i, j] = weight[j] * data[i, j]
for j in range(m):
Z_Plus[j] = max(Zs.T[j])
Z_Minus[j] = min(Zs.T[j])
result = np.ones(n)
for i in range(n):
S_Plus, S_Minus = 0, 0
for j in range(m):
S_Plus += (data[i, j] - Z_Plus[j]) ** 2
S_Minus += (data[i, j] - Z_Minus[j]) ** 2
result[i] = (np.sqrt(S_Minus)) / (np.sqrt(S_Plus) - np.sqrt(S_Minus))
return result, [Z_Plus, Z_Minus], weight
if __name__ == "__main__":
data = np.loadtxt("./data/entropy.txt", delimiter=",")
nIndex = negativeIndex(data)
[result, sol, weight] = topsis(nIndex)
print("\n\nResults:")
print(result)
print("\n\nSolutions:")
print(sol)
print("\n\nWeights:")
print(weight)