JupyterNotebook做层次分析法(AHP)权重计算

2021-8-12 17:44| 发布者: Fuller| 查看: 4909| 评论: 4

摘要: 我们假定有一个决策问题,就是某个部门经理要选一个副手,从3个人中选,选择的准则是品德、才能、资历、年龄、与同僚的关系。每个候选人在每个准则上都有对应的打分,即便都是准确的打分,要计算出来选谁也要费些周 ...

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)权重计算

1

鲜花

握手

雷人

路过

鸡蛋

刚表态过的朋友 (1 人)

发表评论

最新评论

评论 KyleJin 2024-3-28 08:31
大佬牛逼,感谢分享
评论 Fuller 2021-8-31 09:11
15964002091: 这个模板可以在上传下吗,想学习下,感谢。
notebook已经上传到文章中了
评论 Fuller 2021-8-30 22:45
15964002091: 这个模板可以在上传下吗,想学习下,感谢。
等明天上班后上传
评论 15964002091 2021-8-30 21:54
这个模板可以在上传下吗,想学习下,感谢。

查看全部评论(4)

GMT+8, 2024-12-22 14:02