评价算法
熵权法
上面箭头的一步用到了带权重的距离公式。
上面是某种求权重的方法,合理就行。
但是在使用熵权法的时候,一定要注意用的是规范化矩阵再用熵权法求权重。
规范化之前一定要判断每一列的性质
#熵权法:
import xlrd
import numpy as np
#读数据并求熵
path=u"D:\\LearningResource\\myLearningData\\hostital.xls"
hn,nc=1,1
#hn为表头行数,nc为表头列数
sheetname=u'Sheet1'
def readexcel(hn,nc):
data = xlrd.open_workbook(path)
table = data.sheet_by_name(sheetname)
nrows = table.nrows
data=[]
for i in range(hn,nrows):
data.append(table.row_values(i)[nc:])
return np.array(data)
def entropy(data0):
#返回每个样本的指数
#样本数,指标个数
n,m=np.shape(data0)
#一行一个样本,一列一个指标
#下面是归一化
maxium=np.max(data0,axis=0)
minium=np.min(data0,axis=0)
data= (data0-minium)*1.0/(maxium-minium)
##计算第j项指标,第i个样本占该指标的比重
sumzb=np.sum(data,axis=0)
data=data/sumzb
#对ln0处理
a=data*1.0
a[np.where(data==0)]=0.0001
# #计算每个指标的熵
e=(-1.0/np.log(n))*np.sum(data*np.log(a),axis=0)
print(e)
# #计算权重
w=(1-e)/np.sum(1-e)
recodes=np.sum(data0*w,axis=1)
return recodes
data=readexcel(hn,nc)
grades=entropy(data)
print(grades)
Topsis法
TOPSIS(逼近理想解)算法原理详解与代码实现 - 知乎 (zhihu.com)
(98条消息) TOPSIS优劣解距离法–代码部分_丰丰小白的博客-CSDN博客
*个人理解:针对存在多项指标,多个方案的方案评价分析方法,也就是根据已存在的一份数据,判断数据中各个方案的优劣。中心思想是首先确定各项指标的最优理想值(正理想值)和最劣理想值(负理想解),所谓正理想值是一设想的最好值(方案),它的的各个属性值都达到各候选方案中最好的值,而负理想解是另一设想的最坏的值(方案),然后求出各个方案与正理想值和负理想值之间的加权欧氏距离,由此得出各方案与最优方案的接近程度,作为评价方案的优劣标准,最后得到各个方案的优劣值。*
一、topsis算法
1.1 TOPSIS算法的原理
TOPSIS法(Technique for Order Preference by Similarity to Ideal Solution)可翻译为逼近理想解排序法,国内常简称为优劣解距离法
TOPSIS 法是一种常用的综合评价方法,其能充分利用原始数据的信息,其结果能精确地反映各评价方案之间的差距。
为了对众多方案给出一个排序,在给出所有方案之后,可以根据这些数据,构造出一个所有方案组成的系统中的理想最优解和最劣解。而TOPSIS的想法就是,通过一定的计算,评估方案系统中任何一个方案距离理想最优解和最劣解的综合距离。如果一个方案距离理想最优解越近,距离最劣解越远,我们就有理由认为这个方案更好。那理想最优解和最劣解又是什么呢?很简单,理想最优解就是该理想最优方案的各指标值都取到系统中评价指标的最优值,最劣解就是该理想最劣方案的各指标值都取到系统中评价指标的最劣值。
理想最优解中的数据都是各方案中的数据,而不要选择方案中没有的数据,理想最劣解同理。
如何衡量某一个方案与理想最优解和最劣解的综合距离呢?
TOPSIS基本思想是用下面这个表达式进行衡量:
可以发现,如果方案取到了理想最优解,其表达式取值为1;如果方案取到了理想最劣解,其表达式取值为0。我们便可以用这个表达式来衡量系统中某一个方案距离理想最优解和最劣解的综合距离,也直接用它给方案进行打分。
当然这个公式只是一个基本的思路,实际上,为了更准确与合理,会对该公式进行优化。
1.2 TOPSIS算法的实现
在了解TOPSIS算法的基本思想后就是对相应参数的计算了,从上面的描述可以知道,除了要对该公式进行改进之外,因为涉及到数据之间的比较,还需要对方案数据进行处理,消除量纲以及范围太大带来的一系列问题。
二、数据预处理
2.1 数据正向化处理
在处理数据时,有些指标的数据越大越好,有些则是越小越好,有些又是中间某个值或者某段区间最好。我们可以对其进行“正向化处理”,使指标都可以像考试分数那样,越大越好。
将指标分为四类,如下表所示。
三、TOPSIS算法实现
五、TOPSIS算法示例
5.1 TOPSIS算法示例
对一个需要根据学生智商和情商进行排名的数据:原始数据矩阵如下
对其进行正向化:
对其进行标准化:
计算与最优解和最劣解的距离:
注:指标权重默认是相同的,我们之前使用熵权法对权重进行一定的处理,并将权重带入进行计算。
实际上这很可能并不具有合理性。实际上两种算法在提取信息方面都做到了同类数据相差越大越重要。
最后计算得分给出排名:
这个例子告诉我们,成绩很重要,但是情商更重要。小王虽然只考了60分,但也及格了,而且他从不与人争吵,所以我们可以给他一个最好的评价。
5.2 TOPSIS算法扩展
从上面计算各自与最优解和最劣解的距离时,我们看到,每一项指标的权重是一样的。
在实际问题中,不同的指标重要程度可能是不一样的。例如评奖学金的时候,成绩往往是最重要的,之后还有参与活动分,志愿服务分等等,他们的权重又低一点。因此,在实际的应用中,我们也可以给指标进行赋权,将权重放到计算距离的公式中。
考虑权重后,不同指标对最后的影响不一样,考虑权重的评价往往是实际生活中很常见的一种评价方式。
关于权重的选取也有不同的方法,比如层次分析法(主观给出)、熵权法等等。
注意:在这个地方请注意:不可以贸然使用熵权法,例如,在奖学金评定过程中,如果同学们的成绩以外的其他活动或者志愿服务分分布差异比较大,那使用熵权法就会导致这些指标的权重超过学习成绩,这不是我们希望看到的结果,所以这个时候采用主观赋权法:如层次分析法是更加合理的。
六、程序源码
TOPSIS.m程序
clear all
clc
%% 导入数据
% (1)在工作区右键,点击新建(Ctrl+N),输入变量名称为X
% (2)双击进入X,输入或拷贝数据到X
% (3)关掉这个窗口,点击X变量,右键另存为,保存为mat文件
% (4)注意,代码和数据要放在同一个目录下哦,且Matlab的当前文件夹也要是这个目录。
load data_water_quality.mat
%% 数据预处理_正向化
[n,m] = size(X);
disp(['共有' num2str(n) '个评价对象, ' num2str(m) '个评价指标'])
Judge = input(['这' num2str(m) '个指标是否需要经过正向化处理,需要请输入1 ,不需要输入0: ']);
if Judge == 1
Position = input('请输入需要正向化处理的指标所在的列,例如[2,3,6]: '); %[2,3,4]
disp('请输入需要处理的这些列的指标类型(1:极小型, 2:中间型, 3:区间型) ')
Type = input('例如[1,3,2]: '); %[2,1,3]
% 注意,Position和Type是两个同维度的行向量
for i = 1 : size(Position,2)%对每一列进行正向化处理
X(:,Position(i)) = Positivization(X(:,Position(i)),Type(i),Position(i));
% 第一个参数是要正向化处理的那一列向量 X(:,Position(i))
% 第二个参数是对应的这一列的指标类型(1:极小型, 2:中间型, 3:区间型)
% 第三个参数是告诉函数我们正在处理的是原始矩阵中的哪一列
% 返回值返回正向化之后的指标
end
disp('正向化后的矩阵 X = ')
disp(X)
end
%% 数据预处理_标准化
Z = X ./ repmat(sum(X.*X) .^ 0.5, n, 1);
disp('标准化矩阵 Z = ')
disp(Z)
%% 指标权重赋值
disp("请输入是否需要增加权重向量,需要输入1,不需要输入0")
Judge = input('请输入是否需要增加权重: ');
if Judge == 1
disp(['有多少个指标就输入多少个权重数(权重和为1),如[0.25,0.25,0.5]']);
weigh = input(['请输入输入' num2str(m) '个权重: ']);
if abs(sum(weigh) - 1)<0.000001 && size(weigh,1) == 1 && size(weigh,2) == m % 这里要注意浮点数的运算是不精准的。
else
weigh = input('你输入的有误,请重新输入权重行向量: ');
end
else
weigh = ones(1,m) ./ m ; %如果不需要加权重就默认权重都相同,即都为1/m
end
%% 计算与最大值的距离和最小值的距离,并算出得分
D_P = sum([(Z - repmat(max(Z),n,1)) .^ 2 ] .* repmat(weigh,n,1) ,2) .^ 0.5; % D+ 与最大值的距离向量
D_N = sum([(Z - repmat(min(Z),n,1)) .^ 2 ] .* repmat(weigh,n,1) ,2) .^ 0.5; % D- 与最小值的距离向量
S = D_N ./ (D_P+D_N); % 未归一化的得分
disp('最后的得分为:')
stand_S = S / sum(S)% 归一化的得分
[sorted_S,index] = sort(stand_S ,'descend')%对得分进行排序并返回原来的位置
plot(sorted_S,'r-o')
xmin=1;xmax = size(sorted_S,1);
ymin = 0;ymax = max(sorted_S)+min(sorted_S);
axis([xmin xmax ymin ymax]); % 设置坐标轴在指定的区间
grid on
xlabel('方案');ylabel('分数');%坐标轴表示对bai象标签
title('TOPSIS算法最终评分排序')
正向化处理函数Positivization.m程序
function [posit_x] = Positivization(x,type,i)
% 输入变量有三个:
% x:需要正向化处理的指标对应的原始列向量
% type: 指标的类型(1:极小型, 2:中间型, 3:区间型)
% i: 正在处理的是原始矩阵中的哪一列
% 输出变量posit_x表示:正向化后的列向量
if type == 1 %极小型
disp(['第' num2str(i) '列是极小型,正在正向化'] )
posit_x = Min2Max(x); %调用Min2Max函数来正向化
disp(['第' num2str(i) '列极小型正向化处理完成'] )
disp('~~~~~~~~~~~~~~~~~~~~分界线~~~~~~~~~~~~~~~~~~~~')
elseif type == 2 %中间型
disp(['第' num2str(i) '列是中间型'] )
best = input('请输入最佳的那一个值(中间的那个值): ');
posit_x = Mid2Max(x,best);
disp(['第' num2str(i) '列中间型正向化处理完成'] )
disp('~~~~~~~~~~~~~~~~~~~~分界线~~~~~~~~~~~~~~~~~~~~')
elseif type == 3 %区间型
disp(['第' num2str(i) '列是区间型'] )
a = input('请输入区间的下界: ');
b = input('请输入区间的上界: ');
posit_x = Inter2Max(x,a,b);
disp(['第' num2str(i) '列区间型正向化处理完成'] )
disp('~~~~~~~~~~~~~~~~~~~~分界线~~~~~~~~~~~~~~~~~~~~')
else
disp('没有这种类型的指标,请检查Type向量中是否有除了1、2、3之外的其他值')
end
end
以下面这个例子为例:
首先导入数据,然后运行程序,结果如下:
关于运行后没有min2max,是因为没有定义极小、极大、中间型函数,具体代码在CSDN:
**TOPSIS优劣解距离法–代码部分_丰丰小白的博客-CSDN博客_优劣解距离法代码**
TOPSIS优劣解距离法–代码部分
下面要介绍的是TOPSIS的代码书写方法,TOPSIS总体分为三步:
1、矩阵正向化
2、正向化矩阵标准化
3、计算评分并归一化
下面根据这三步开始我们的代码编写:
第一步:导入数据
把数据复制到工作区,并将这个矩阵命名为x
① 在工作区右键,点击新建(ctrl+n),输入变量名称X
② 在Excel中复制数据,再回到X变量中粘贴数据
③ 右键X另存为,保存为mat文件,以后调用X只需要用load命令即可加载数据(这里我保存的mat文件命名为X.mat)
④ 代码和数据需要放在同一个目录下哦
第二步:判断是否需要正向化,并进行正向化
[n,m]=size(X);
disp(['共有num2str(n)'个评价对象'个评价对象'num2str(m)'个评价指标']
%用向量的方法拼接并输出字符串
Judge=input(['这'num2str(m)'个指标是否经过正向化处理,需要请输入1,不需要请输入0:']);
%使用input函数,来给Judge赋值
if Judge==1
Position=input('请输入需要正向化处理的指标所在的列,例如第2、3、6三列需要处理,那么你输入[2,3,6]:');
%使用input函数确定需要正向化处理的位置
disp('请输入需要处理的这些列的标准类型(1:极小型,2:中间型,3:区间型)')
Type=input('例如:第2列是极小型,第3列是区间型,第6列是中间型,就输入[1,3,2]:');
%使用intput函数确定需要处理列的指标类型
%需要注意的是Position和Type是相同维度的行向量
for i=1:size(Position,2)%这里得出的是循环次数,也就是每一个需要正向化的列都做一次循环
X(:,Position(i))=Positivization(X(:,Position(i)),Type(i),Position(i));
%Positivization是我们自己定义的函数,其作用是进行正向化,一共接受3个参数
%第一个参数就是要正向化处理的那一列向量X(:,Position(i))
%第二个参数是对应的这一列的指标类型(1:极小型,2:中间型,3:区间型)
%第三个参数是告诉函数我们正在处理的是原始矩阵中的哪一列
%该函数有一个返回值,它返回正向化之后的指标那一列,我们可以直接给我们原始要处理的列向量进行赋值
end
disp('正向化后的矩阵X=')
disp(X)
end
接下来开始定义我们的Positivization函数,函数不可以直接放在主函数中,和大多数语言不同,函数需要在matlab中单独定义一个m文件,并且函数文件需要和主函数放在同一个目录,也就是同一个文件夹当中。首先定义三种指标的转化函数:
极小型转化为极大型:Min2Max
%function[输出变量]=函数名(输入变量)
%函数最后需要用end结尾
function[posit_x]=Min2Max(x)
posit_x=max(x)-x;
end
function[posit_x]=Mid2Max(x,best)
M=max(abs(x-best));
posit_x=1-abs(x-best)/M;
end
function[posit_x]=Inter2Max(x,a,b)
r_x=size(x,1);%表示列向量的行数(row of x),来找到循环的次数
M=max([a-min(x),max(x)-b]);
posit_x=zeros(r_x,1) %将posit_x初始化为一个全为0的列向量
for i=1:r_x
if x(i)<a
posit_x(i)=1-(a-x(i)/M;
elseif x(i)>b
posit_x(i)=1-(x(i)-b)/M;
else
posit_x(i)=1;
end
end
end
接下来开始写Positivization函数:
%function[输出变量]=函数名(输入变量)
%函数最后需要用end结尾
%输出变量和输入变量可以有多个,中间用逗号隔开
function[posit_x]=Positivization(x,type,i)%这里定义函数参数,其中均为形参
%posit_x表示正向化后的列向量
%x表示需要正向化处理的指标对应的原始列向量
%type表示指标的类型(1:极小型,2:中间型,3:区间型)
%i表示正在处理的是原矩阵中的哪一列
if type==1 %极小型
disp(['第'num2str(i)'列是极小型,正在正向化'])
posit_x=Min2Max(x);%调用Min2Max函数来进行正向化
disp(['第'num2str(i)'列极小型正向化处理完毕'])
disp('~~~~~~~~~~~~~~~~~~~分界线~~~~~~~~~~~~~~~~~~~')
elseif type==2%中间型
disp(['第'num2str(i)'列是中间型'])
best=input('请输入最佳的值:');
posit_x=Mid2Max(x,best);
disp(['第'num2str(i)'列中间型正向化处理完毕'])
disp('~~~~~~~~~~~~~~~~~~~分界线~~~~~~~~~~~~~~~~~~~')
elseif type==3 %区间型
disp(['第'num2str(i)'列是区间型'])
a=input('请输入区间的上界');
b=input('请输入区间的下界');
posit_x=Inter2Max(x,a,b);
disp(['第'num2str(i)'列区间型正向化处理完毕'])
disp('~~~~~~~~~~~~~~~~~~~分界线~~~~~~~~~~~~~~~~~~~')
end
end
第三步:对正向化后的矩阵进行标准化
Z=X./repmat(sum(X.*X).^0.5,n,1);
disp('标准化矩阵Z=')
disp(Z)
第四步:计算与最大值的距离和与最小值的距离,并计算出得分
D_P=sum([(Z-repmat(max(Z),n,1)).^2],2).^0.5;%D+与最大值的距离
D_N=sum([(Z-repmat(min(Z),n,1)).^2],2).^0.5;%D-与最小值的距离
S=D_N./(D_P+D_N);%未归一化的得分
disp('最后的得分为:')
stand_S=S/sum(S)
[sortde_s,index]=sort(stand_S,'descend')
%这里使用sort函数进行排序,sort函数默认升序排列,加入参数'descend'为降序排列
%返回值sortde_s为降序排列后的矩阵,index为排列后的序号。
python版一个综合练习题
现有一份关于各个河流的数据,其中oxygen表示水中含氧比例,该比例越大越好;PH表示水体的酸碱性,该值越接近7越好;bacteria表示水中细菌数量,该值越小越好;water plants表示水中水草数量,该值最好是介于[500,1000]。
import pandas as pd
path = 'C:UsersCaraDesktopwaterquality.xlsx'
rawdata = pd.read_excel(path)
#一、将所有指标正向化
#1.处理PH指标,它属于中间型指标,将其转化为极大型指标
#PH指标最优值为7
PHbest = 7
#(1)构造PH变量的中间变量M_PH
PH = list(rawdata['PH'])
lst1 = []
for i in range(0,len(PH)):
d = abs(PH[i] - PHbest)
lst1.append(d)
M_PH = max(lst1)
#(2)使用公式将PH指标转化为极大型指标,保留4位小数
PH_bar = [round(1-abs(i-PHbest)/M_PH,4) for i in PH]
#2.处理Bacteria指标,它属于极小型指标,将其转化为极大型指标
Bacteria = list(rawdata['Bacteria'])
#用 max-x的方法正向化
max_Bacteria = max(Bacteria)
Bacteria_bar = [max_Bacteria - i for i in Bacteria]
#3.处理WaterPlants指标,它属于区间型指标,将其转化为极大型指标
#最佳区间为[500,1000]
WP_a = 500
WP_b = 1000
#构造中间变量M_WP
WP = list(rawdata['WaterPlants'])
min_WP = min(WP)
max_WP = max(WP)
M_WP = max(WP_a - min_WP,max_WP - WP_b)
#根据转化公式,将WaterPlants指标转化为极大型指标
WP_bar = []
for i in range(0,len(WP)):
if WP[i] >= WP_a and WP[i] <= WP_b:
x = 1
elif WP[i] < WP_a:
x = 1 - (WP_a - WP[i])/M_WP
else:
x = 1 - (WP[i] -WP_b)/M_WP
WP_bar.append(x)
#二、所有指标进行数据标准化处理
#1.Oxygen_bar指标标准化
import numpy as np
Oxygen_bar = list(rawdata['Oxygen'])
lst2 = []
for i in range(0,len(Oxygen_bar)):
p = pow(Oxygen_bar[i], 2)
lst2.append(p)
c = np.sqrt(sum(lst2))#构造出标准化公式中的分母部分
Oxygen_bar_standardized = [i/c for i in Oxygen_bar]
#2.PH_bar指标标准化
lst3 = []
for i in range(0,len(PH_bar)):
p = pow(PH_bar[i], 2)
lst3.append(p)
c = np.sqrt(sum(lst3))#构造出标准化公式中的分母部分
PH_bar_standardized = [i/c for i in PH_bar]
#3.Bacteria_bar指标标准化
lst4 = []
for i in range(0,len(Bacteria_bar)):
p = pow(Bacteria_bar[i], 2)
lst4.append(p)
c = np.sqrt(sum(lst4))#构造出标准化公式中的分母部分
Bacteria_bar_standardized = [i/c for i in Bacteria_bar]
#4.WP_bar指标标准化
lst5 = []
for i in range(0,len(WP_bar)):
p = pow(WP_bar[i], 2)
lst5.append(p)
c = np.sqrt(sum(lst5))#构造出标准化公式中的分母部分
WP_bar_standardized = [i/c for i in WP_bar]
#三、根据评分公式进行评分
#1.在评分前,将标准化后的变量拼接起来
stand_data = pd.DataFrame({'River':rawdata['River'],
'Oxygen':pd.Series(Oxygen_bar_standardized),
'PH':pd.Series(PH_bar_standardized),
'Bacteria':pd.Series(Bacteria_bar_standardized),
'WaterPlants':pd.Series(WP_bar_standardized)}
)
#2.计算各指标的最大值和最小值,形成两个最值向量
stand_data.describe()
#各变量最大值
max_values = stand_data.describe().iloc[[7],:]
#各变量最小值
min_values = stand_data.describe().iloc[[3],:]
#3.计算每个河流向量到各变量最小值、最大值的距离
#计算每个河流向量到各变量最小值的距离
from scipy.spatial import distance
sd_data = stand_data.copy().drop(columns=['River'])
dst_to_min = []
for i in range(0,len(stand_data)):
v = sd_data.iloc[[i], :]
d = distance.euclidean(v, min_values)
dst_to_min.append(d)
#计算每个河流向量到各变量最大值的距离
dst_to_max = []
for i in range(0,len(stand_data)):
v = sd_data.iloc[[i], :]
d = distance.euclidean(v, max_values)
dst_to_max.append(d)
#根据评分公式,计算每个河流的得分
#S = dst_to_min /(dst_to_max + dst_to_min)
Score = []
for i in range(0,len(dst_to_min)):
s = dst_to_min[i]/(dst_to_max[i] + dst_to_min[i])
Score.append(s)
#得分归一化
total = sum(Score)
Score_normalized = [i/total for i in Score]
#将归一化后的得分添加到stand_data数据框中
stand_data['Score_normalized'] = Score_normalized
print(stand_data)
计算出的得分结果(Score_normalized):