TOPSIS(优劣解距离法)【附Python实现代码及可视化代码】

目录


TOPSIS法是一种常用的综合评价方法,其能充分利用原始数据的信息,其结果能精确反应各评价方案之间的差距。

用TOPSIS法来评定一个人是不是值得恋爱。比如,现在有4个人,他们分别为Terry,xiaofang,smike,jerry.以下为根据他们身边的好朋友对他们的评价得分:

简单的评分方法是直接按评价得分高低排名评分:

不过要用修正后的排名,因为评分越高越好

但是这样会出现一个问题,可以随便修改得分,只要排名不变,评分也不会改变。

所以,我们不能直接按得分排名评价,要构造计算评分的公式:

TOPSIS法步骤如下:

一、指标正向化

好友评分是越大(高)越好,这样的指标称为极大型指标(效益型指标)。

恋爱次数是越少(低)越好,这样的指标称为极小型指标(成本型指标)

中间型指标:指标值既不要太大也不要太小,取某特定值最好(如水质量评估 PH 值 )

区间型指标:指标值落在某个区间内最好,例如人的体温在36°~37°这个区间比较好。

所以,我们必须的统一指标类型,将所有的指标转化为极大型,即指标正向化。

1.极小型指标->极大型指标

公式:

如果所有的元素均为正数,那么也可以使用

2.中间型指标->极大型指标

举例:

3.区间型指标->极大型指标

 上面不好理解也可以看下面这个公式

二、标准化处理

为了消去不同指标量纲的影响,需要对已经正向化的矩阵进行标准化处理。

比如:

三、计算得分并归一化(不带权重)

归一化之后就可以根据数值大小进行排名

区别归一化和标准化:

归一化的目的:为了让结果更容易解释,对结果有一个更加清晰直观的印象。

标准化的目的:消除量纲的影响。

四、计算得分并归一化(带权重)

如何计算权重?

基于熵权法的赋值

熵权法

熵权法是一种客观赋权方法

原理:

指标的变异程度越小,所反映的信息量也越少,其对应的权值也应该越低。

如何度量信息量的大小?

越有可能发生的事情,信息量越少,

越不可能发生的事情,信息量就越多。

怎么衡量事情发生的可能性大小 ?

概率

如何度量信息量的大小?

信息熵越大,则它的值能补充的信息量越大,说明这个值已有的信息量越小,所以信息量越小

熵权法计算步骤:

1)判断输入的矩阵是否存在负数

如果存在负数,就要重新标准化到非负区间。

2)计算第j项指标下第i个样本所占的比重,并将其看作相对熵计算中用到的概率

3)计算每个指标的信息熵,并计算信息效用值,并归一化得到每个指标的熵权

五、代码

import numpy as np
import pandas as pd


data = pd.DataFrame(
        {'人均专著': [0.1, 0.2, 0.4, 0.9, 1.2], 
         '生师比': [5, 6, 7, 10, 2], 
         '科研经费': [5000, 6000, 7000, 10000, 400],
         '逾期毕业率': [4.7, 5.6, 6.7, 2.3, 1.8]}, 
          index=['院校' + i for i in list('ABCDE')])

 对五所研究生院进行评价,构造数据如下:

 5.1 指标正向化

# 指标正向化
# 结合公式理解代码
# 需要根据题目选择具体的处理方法


# 极小型转为极大型指标
def dataDirection_1(datas, offset=0):
    def normalization(data):
        return 1 / (data + offset)

    return list(map(normalization, datas))


# 中间型指标转为极大型指标
def dataDirection_2(datas, x_min, x_max):
    def normalization(data):
        if data <= x_min or data >= x_max:
            return 0
        elif data > x_min and data < (x_min + x_max) / 2:
            return 2 * (data - x_min) / (x_max - x_min)
        elif data < x_max and data >= (x_min + x_max) / 2:
            return 2 * (x_max - data) / (x_max - x_min)

    return list(map(normalization, datas))

# 区间型指标转为极大型指标
# [x_min, x_max]最佳稳定区间, [x_minimum, x_maximum]容忍区间
def dataDirection_3(datas, x_min, x_max, x_minimum, x_maximum):  
    def normalization(data):
        if data >= x_min and data <= x_max:
            return 1
        elif data <= x_minimum or data >= x_maximum:
            return 0
        elif data > x_max and data < x_maximum:
            return 1 - (data - x_max) / (x_maximum - x_max)
        elif data < x_min and data > x_minimum:
            return 1 - (x_min - data) / (x_min - x_minimum)

    return list(map(normalization, datas))

例题数据处理:

# 极小型指标转为极大型
minimum_list = dataDirection_1(data.loc[:,"逾期毕业率"])
minimum_array = np.array(minimum_list)
minimum_4f = np.round(minimum_array, 6)
print(minimum_4f)

# 区间型指标转为极大型
maximum_list = dataDirection_3(data.loc[:,"生师比"], 5, 6, 2, 12)
maximum_array = np.array(maximum_list)
maximum_4f = np.round(maximum_array, 6)
print(maximum_4f)

结果:
[0.212766 0.178571 0.149254 0.434783 0.555556]
[1.       1.       0.833333 0.333333 0.      ]
# 指标正向化结果
index_Isotropy = pd.DataFrame()
index_Isotropy["人均专著"] = data["人均专著"]
index_Isotropy["生师比"] = maximum_4f
index_Isotropy["科研经费"] = data["科研经费"]
index_Isotropy["逾期毕业率"] = minimum_4f
print(index_Isotropy)

 5.2 标准化处理

# 这里采用归一化,数据处于0-1之间,便于可视化
data_normalization = index_Isotropy / np.sqrt((index_Isotropy ** 2).sum())
print(data_normalization)

 5.3 计算得分并排序(人工赋权重)

def topsis(data, weight=None):


    # 最优最劣方案(最大值Z^+ 和 最小值)
    Z = pd.DataFrame([data.max(), data.min()], index=['正理想解', '负理想解'])

    # 距离
    weight = entropyWeight(data) if weight is None else np.array(weight)
    Result = data.copy()
    Result['正理想解'] = np.sqrt(((data - Z.loc['正理想解']) ** 2 * weight).sum(axis=1)) # 评价对象与最大值的距离
    Result['负理想解'] = np.sqrt(((data - Z.loc['负理想解']) ** 2 * weight).sum(axis=1))

    # 综合得分指数
    Result['综合得分指数'] = Result['负理想解'] / (Result['负理想解'] + Result['正理想解'])  
    Result['排序'] = Result.rank(ascending=False)['综合得分指数']

    return Result, Z, weight

# 人工赋权重的结果
weight = [0.2, 0.3, 0.4, 0.1]
Result, Z, weight = topsis(data_normalization, weight)

 5.4 熵权法

def entropyWeight(data):
    data = np.array(data)
    # 计算第j个指标下第i个样本所占的比重,相对熵计算中用到的概率
    P = data / data.sum(axis=0)  # 压缩行
    # 计算熵值
    E = np.nansum(-P * np.log(P) / np.log(len(data)), axis=0)
    # 信息效用值
    d = (1 - E)
    # 计算权系数
    W = d / d.sum()
    return W

entropyWeight(data)

结果:
array([0.41803075, 0.14492264, 0.28588943, 0.15115718])

# 将计算出来的权重代入5.3计算就可以

5.5 数据可视化

这里是对原始数据归一化后以及计算出来的正负理想解进行可视化,便于观察比较各院校特征

使用了雷达图

# 把归一化的结果列的顺序改一下,把‘生师比’放第一个,便于下图展示,不改也可以
data_normalization=data_normalization[['生师比','人均专著',  '逾期毕业率', '科研经费']]
data_normalization
from math import pi
import matplotlib.pyplot as plt


# 目标数量
categories = list(data_normalization)[0:]
N = len(categories)

# 角度
angles = [n / float(N) * 2 * pi for n in range(N)]
angles += angles[:1]

# 绘图初始化
plt.figure(dpi=150)
plt.style.use('ggplot')
ax = plt.subplot(111, polar=True)

# 设置第一处
ax.set_theta_offset(pi / 2)  # 设置最上面为0°
ax.set_theta_direction(-1)  # 设置正方形为顺时针

# 添加背景信息
plt.title("研究生院试评估")
plt.xticks(angles[:-1], categories)  # 改变轴标签rotation = 30
plt.xticks(rotation = pi/4,fontsize=6)
ax.set_rlabel_position(30)   # 极径标签显示位置
# ax.set_rgrids(np.arange(0.1,0.9,0.1))
plt.yticks(np.arange(0.1,0.9,0.1), ["0.1", "0.2", "0.3","0.4","0.5","0.6","0.7","0.8"], color="grey", size=5)  # 设置极径标签区间
plt.ylim(0, 0.8)  # 设置极径范围

# 添加数据图

# 第一个
values = data_normalization.loc["院校A"].values.flatten().tolist()
values += values[:1]  # 首尾相连
ax.plot(angles, values, linewidth=1, linestyle='solid', color='#d62728',marker='.',markersize=4,label="院校A")
ax.fill(angles, values, '#d62728', alpha=0.2)  # 填充线条

# 第二个
values = data_normalization.loc["院校B"].values.flatten().tolist()
values += values[:1]
ax.plot(angles, values, linewidth=1, linestyle='solid',color='#1f77b4',marker='.',markersize=4, label="院校B")
ax.fill(angles, values, '#1f77b4', alpha=0.2)

# 第三个
values = data_normalization.loc["院校C"].values.flatten().tolist()
values += values[:1]
ax.plot(angles, values, linewidth=1, linestyle='solid', color='#9467bd',marker='.',markersize=4,label="院校C")
ax.fill(angles, values, '#9467bd', alpha=0.2)

# 第四个
values = data_normalization.loc["院校D"].values.flatten().tolist()
values += values[:1]
ax.plot(angles, values, linewidth=1, linestyle='solid',color='#7f7f7f', marker='.',markersize=4, label="院校D")
ax.fill(angles, values, '#7f7f7f', alpha=0.2)

# 第五个
values = data_normalization.loc["院校E"].values.flatten().tolist()
values += values[:1]
ax.plot(angles, values, linewidth=1, linestyle='solid',color='#ff7f0e',marker='.',markersize=4, label="院校E")
ax.fill(angles, values, '#ff7f0e', alpha=0.2)

# 最优解
values =Z.loc["正理想解"].values.flatten().tolist()
values += values[:1]
ax.plot(angles, values, linewidth=1, linestyle='solid',color='#2ca02c',marker='.',markersize=4, label="最优解")
ax.fill(angles, values, '#2ca02c', alpha=0.2) 

# 最劣解
values =Z.loc["负理想解"].values.flatten().tolist()
values += values[:1]
ax.plot(angles, values, linewidth=1, linestyle='solid',color='#e377c2',marker='.',markersize=4, label="最劣解")
ax.fill(angles, values, '#e377c2', alpha=0.2) 

# 添加图例
plt.legend(loc='upper right', bbox_to_anchor=(1.5, 1))

# 显示
plt.show()

 可视化结果如下:

参考资料:

原理部分参考了清风数模视频和一篇博客,具体哪篇之前写的时候忘记下来了

代码部分参考了下面知乎这篇,原理讲的也很好,不过这篇没有雷达图的代码,本文补充了这部分代码

TOPSIS法(优劣解距离法)介绍及 python3 实现 – 知乎 (zhihu.com)

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
社会演员多的头像社会演员多普通用户
上一篇 2023年5月24日
下一篇 2023年5月24日

相关推荐