1,本Notebook介绍 今天这个Notebook,基于简单的测试数据,进行层次分析法(AHP)权重计算算法测试。 1.1,层次分析法(AHP)介绍 参看《层次分析法(AHP)简介》 层次分析法(Analytic Hierarchy Process,简称 AHP)对一些较为复杂、较为模糊的问题作出决策的简易方法,它特别适用于那些难于完全定量分析的问题。它是美国运筹学家 T.L.Saaty教授于上世纪 70 年代初期提出的一种简便、灵活而又实用的多准则决策方法。层次分析法将与决策有关的元素分解成目标、准则、方案等层次,在此基础之上进行定性和定量分析的决策方法。 1.2,测试数据 参考《【AHP】层次分析法原理与Python实现》,我们假定有一个决策问题,就是某个部门经理要选一个副手,从3个人中选,选择的准则是品德、才能、资历、年龄、与同僚的关系。每个候选人在每个准则上都有对应的打分,即便都是准确的打分,要计算出来选谁也要费些周章。更大的问题是,面对这样的决策,就没法给每个人的每个准则打分,但是,我们可以两两比较给出一个比值,起码孰轻孰重的顺序看起来是对的。通过AHP计算方法,可以验证此前的估计是否符合一定的一致性要求,如果符合的话,得到一个总的评分顺序,也就是得到了最佳候选人。 2,引入numpy库 Numpy是一个常用的Python科学技术库,通过它可以快速对数组进行操作,包括形状操作、排序、选择、输入输出、离散傅立叶变换、基本线性代数,基本统计运算和随机模拟等。许多Python库和科学计算的软件包都使用Numpy数组作为操作对象,或者将传入的Python数组转化为Numpy数组,因此在Python中操作数据离不开Numpy。 Numpy的核心是ndarray对象,由Python的n维数组封装而来,但通过C语言预编译相关的数组操作,因此比原生Python具有更高的执行效率,但仍然使用Python语言编码,这样就同时具有简洁的代码和高效的运行速度。ndarry与数组有些区别值得注意,numpy数组中的元素都具有相同的类型,并且在创建时就确定了固定的大小,这与Python数组对象可以动态增长不同。 # coding:utf-8 import numpy as np 3,定义AHP类 定义一个Python的类,主要目的是方便使用,因为同样的运算要用在准则层和方案层,如果准则有n个,方案有m个,那么在方案层实际上要使用n次。所以,封装成一个类就方便了。AHP类代码摘自《python实现AHP算法(层次分析法)》 3.1,AHP类定义的变量 array: 记录矩阵相关信息 n: 记录矩阵大小 RI_list: 初始化RI值,用于一致性检验 eig_val, eig_vector: 矩阵的特征值和特征向量 max_eig_val: 矩阵的最大特征值 max_eig_vector: 矩阵最大特征值对应的特征向量 CI_val: 矩阵的一致性指标CI CR_val: 矩阵的一致性比例CR 3.2,AHP类定义的方法 一致性判断方法:test_consist 算术平均法求权重:cal_weight_by_arithmetic_method 几何平均法求权重:cal_weight__by_geometric_method 特征值法求权重:cal_weight__by_eigenvalue_method class AHP: """ 相关信息的传入和准备 """ def __init__(self, array): ## 记录矩阵相关信息 self.array = array ## 记录矩阵大小 self.n = array.shape[0] # 初始化RI值,用于一致性检验 self.RI_list = [0, 0, 0.52, 0.89, 1.12, 1.26, 1.36, 1.41, 1.46, 1.49, 1.52, 1.54, 1.56, 1.58, 1.59] # 矩阵的特征值和特征向量 self.eig_val, self.eig_vector = np.linalg.eig(self.array) # 矩阵的最大特征值 self.max_eig_val = np.max(self.eig_val) # 矩阵最大特征值对应的特征向量 self.max_eig_vector = self.eig_vector[:, np.argmax(self.eig_val)].real # 矩阵的一致性指标CI self.CI_val = (self.max_eig_val - self.n) / (self.n - 1) # 矩阵的一致性比例CR self.CR_val = self.CI_val / (self.RI_list[self.n - 1]) """ 一致性判断 """ def test_consist(self): # 打印矩阵的一致性指标CI和一致性比例CR print("判断矩阵的CI值为:",str(self.CI_val)) print("判断矩阵的CR值为:",str(self.CR_val)) # 进行一致性检验判断 if self.n == 2: # 当只有两个子因素的情况 print("仅包含两个子因素,不存在一致性问题") else: if self.CR_val < 0.1: # CR值小于0.1,可以通过一致性检验 print("判断矩阵的CR值为",str(self.CR_val),",通过一致性检验") return True else: # CR值大于0.1, 一致性检验不通过 print("判断矩阵的CR值为",str(self.CR_val),"未通过一致性检验") return False """ 算术平均法求权重 """ def cal_weight_by_arithmetic_method(self): # 求矩阵的每列的和 col_sum = np.sum(self.array, axis=0) # 将判断矩阵按照列归一化 array_normed = self.array / col_sum # 计算权重向量 array_weight = np.sum(array_normed, axis=1) / self.n # 打印权重向量 print("算术平均法计算得到的权重向量为:\n", array_weight) # 返回权重向量的值 return array_weight """ 几何平均法求权重 """ def cal_weight__by_geometric_method(self): # 求矩阵的每列的积 col_product = np.product(self.array, axis=0) # 将得到的积向量的每个分量进行开n次方 array_power = np.power(col_product, 1 / self.n) # 将列向量归一化 array_weight = array_power / np.sum(array_power) # 打印权重向量 print("几何平均法计算得到的权重向量为:\n", array_weight) # 返回权重向量的值 return array_weight """ 特征值法求权重 """ def cal_weight__by_eigenvalue_method(self): # 将矩阵最大特征值对应的特征向量进行归一化处理就得到了权重 array_weight = self.max_eig_vector / np.sum(self.max_eig_vector) # 打印权重向量 print("特征值法计算得到的权重向量为:\n", array_weight) # 返回权重向量的值 return array_weight 4,构造判断矩阵 4.1,构造准则层的判断矩阵 针对目标,我们掂量掂量每个准则相互之间的轻重,用一个比值表示。比如,我们认为从重要性来说: 1. 品德是才能的2倍,品德是资历的7倍,品德是年龄的5倍,品德是同僚关系的5倍。那么为构造出来的行向量就是[1, 2, 7, 5, 5] 2. 才能对品德的比较在上一步已经完成了,才能是资历的4倍,才能是年龄的3倍,才能是关系的3倍,那么构造出来[1/2, 1, 4, 3, 3] 3. 以此类推,得到一下矩阵 a = np.array([[1, 2, 7, 5, 5], [1 / 2, 1, 4, 3, 3], [1 / 7, 1 / 4, 1, 1 / 2, 1 / 3], [1 / 5, 1 / 3, 2, 1, 1], [1 / 5, 1 / 3, 3, 1, 1]]) 4.2,构造方案层判断矩阵 上层准则层有5个准则,需要针对每个准则,掂量掂量两个候选人之间的轻重,毕竟每个人的特长不一样。 上图画了一种情况,作为例子,我们针对品德,构造方案的判断矩阵。面对品德,我们认为两人的重要性来说: 1. 张三是李四的1/3倍,张三是李四的1/8倍,那么行向量就是[1, 1/3, 1/8] 2. 剩下只需要比较李四对王五是1/3倍,那么向量就是[3, 1, 1/3] 3. 对应位置取倒数,那么最后一个向量是[8, 3, 1] 这样就构造出来对应第一个准则的方案的判断矩阵,命名为b1。类似方法用于构造b2, b3, b4, b5 b1 = np.array([[1, 1 / 3, 1 / 8], [3, 1, 1 / 3], [8, 3, 1]]) b2 = np.array([[1, 2, 5], [1 / 2, 1, 2], [1 / 5, 1 / 2, 1]]) b3 = np.array([[1, 1, 3], [1, 1, 3], [1 / 3, 1 / 3, 1]]) b4 = np.array([[1, 3, 4], [1 / 3, 1, 1], [1 / 4, 1, 1]]) b5 = np.array([[1, 4, 1 / 2], [1 / 4, 1, 1 / 4], [2, 4, 1]]) 5,计算权重 上面构造的判断矩阵是一个正互反矩阵,在解决计算权重做决策这个问题时,应该出现过很多方法,比如,我们将尝试的算术平均值等,根据《层次分析法中特征向量法确定权重向量的理论》,求特征向量是最稳定的方法,为什么这么算就可以,看看《理论》那篇文章就能明白个大概。根据《简述层次分析法(AHP)》,把每层计算结果再综合在一起,就是最后的结果。 下面我们就探索一下。 5.1,测试算术平均法求权重 # 得到b1的AHP对象,便于后面使用 ahp1 = AHP(b1) # 算术平均法求权重 weight1 = ahp1.cal_weight_by_arithmetic_method() 输出: 算术平均法计算得到的权重向量为: [0.08199023 0.23644689 0.68156288] 5.2,测试几何平均法求权重 # 几何平均法求权重 weight2 = ahp1.cal_weight__by_geometric_method() 输出: 几何平均法计算得到的权重向量为: [0.68172455 0.2363407 0.08193475] 5.3,测试特征值法求权重并检查一致性 # 特征值法求权重 weight3 = ahp1.cal_weight__by_eigenvalue_method() ahp1.test_consist() 输出: 特征值法计算得到的权重向量为: [0.08193475 0.2363407 0.68172455] 判断矩阵的CI值为: (0.0007708125321126413+0j) 判断矩阵的CR值为: (0.0014823317925243102+0j) 判断矩阵的CR值为 (0.0014823317925243102+0j) ,通过一致性检验 True 6,求综合的权重并选出候选人 《【AHP】层次分析法原理与Python实现》有完整的实现,本Notebook不再赘述,本Notebook仅演练一下计算权重向量和验证一致性,最后,算综合权重实际上就是逐层求加权和。如果一致性一直是True的,那么选出的候选人最后可能是最佳的。 7,下载本Jupyter Notebook 下载源代码请进入:用Python做层次分析法(AHP)权重计算 |