'# Python 层次分析(AHP)
一、背景与问题
层次分析法(Analytic Hierarchy Process, AHP)是一种多准则决策支持方法,由美国运筹学家Thomas Saaty于1970年代提出。它通过将复杂问题分解为层次结构,结合定性与定量分析,为多目标、多方案的决策问题提供系统化解决方案。
在实际项目中,AHP常用于以下场景:
- 产品开发中的功能优先级排序
- 项目资源分配决策
- 供应商选择
- 风险评估模型
- 旅游目的地选择等
但AHP也存在适用限制:
- 仅适用于定性指标量化后的情况
- 无法处理动态变化的决策环境
- 需要决策者提供主观判断矩阵
- 一致性检验对结果有重要影响
二、基本原理
AHP的数学基础是矩阵理论和特征向量分析,其核心流程如下:
构建层次结构模型
- 目标层
- 准则层(指标)
- 方案层
构造判断矩阵
使用1-9标度法进行两两比较:- 1:同等重要
- 3:稍微重要
- 5:明显重要
- 7:强烈重要
- 9:极端重要
- 2、4、6、8:中间值
计算权重向量
- 对判断矩阵进行归一化处理
- 计算各行元素的和
- 求各行的平均值作为权重
- 对判断矩阵进行特征向量分析
一致性检验
- 计算一致性指标CI = (λ_max - n)/(n-1)
- 计算一致性比率CR = CI/RI
- 当CR < 0.1时,认为判断矩阵具有可接受的一致性
三、环境准备
# 安装必要库
pip install numpy pandas scipy核心依赖:
- NumPy:处理矩阵运算
- SciPy:提供矩阵计算工具
- Pandas:数据处理
四、核心实现
1. 判断矩阵构造与权重计算
import numpy as np
def create_judgment_matrix(criteria):
"""创建判断矩阵"""
n = len(criteria)
matrix = np.zeros((n, n))
# 简化输入:使用等重要性矩阵
for i in range(n):
for j in range(n):
matrix[i][j] = 1 if i == j else 0.5 # 仅演示用
return matrix
def calculate_weights(matrix):
"""计算权重向量"""
n = matrix.shape[0]
# 归一化处理
normalized = matrix / matrix.sum(axis=1, keepdims=True)
# 计算行平均值
weights = normalized.mean(axis=1)
return weights
# 示例
criteria = ['价格', '质量', '服务']
matrix = create_judgment_matrix(criteria)
print("判断矩阵:")
print(matrix)
print("\n权重向量:")
print(calculate_weights(matrix))关键点解释:
- 判断矩阵的归一化处理是关键步骤
- 行平均值计算得到各指标的权重
- 实际应用中需使用更复杂的标度方法
2. 一致性检验实现
from scipy import linalg
def calculate_consistency_ratio(matrix):
"""计算一致性比率"""
n = matrix.shape[0]
# 计算最大特征值
eigen_values, eigen_vectors = linalg.eig(matrix)
max_eigen = np.max(eigen_values)
# 计算一致性指标
ci = (max_eigen - n) / (n - 1)
# 查表得到RI值(随机指标)
RI_table = {1:0, 2:0, 3:0.58, 4:0.90, 5:1.12, 6:1.24, 7:1.32, 8:1.41, 9:1.45}
ri = RI_table[n]
# 计算一致性比率
cr = ci / ri if ri != 0 else 0
return cr
# 测试
print("一致性比率:", calculate_consistency_ratio(matrix))关键点解释:
- 使用SciPy的eig函数进行特征值分解
- RI值需要根据矩阵阶数查表
- 当CR≥0.1时需要重新构造判断矩阵
3. 综合权重计算(多层AHP)
def calculate_total_weights(criteria, sub_criteria, sub_weights, weights):
"""计算综合权重"""
n = len(criteria)
total_weights = np.zeros(n)
for i in range(n):
# 获取该准则下的子指标权重
sub_weights_i = sub_weights[i]
# 计算各子指标的贡献
total_weights[i] = np.dot(sub_weights_i, weights)
return total_weights
# 示例数据
sub_criteria = [['价格', '性价比'], ['质量', '耐用性'], ['服务', '响应速度']]
sub_weights = [
[0.6, 0.4],
[0.5, 0.5],
[0.7, 0.3]
]
total_weights = calculate_total_weights(criteria, sub_criteria, sub_weights, weights)
print("综合权重:", total_weights)关键点解释:
- 需要构建多层结构的权重矩阵
- 各层权重需要进行归一化处理
- 可通过递归方式实现多层计算
五、完整案例
旅游目的地选择案例
业务需求:
某公司需要从3个候选城市(A、B、C)中选择旅游目的地,考虑价格、质量、服务三个指标。
数据准备:
# 候选城市
cities = ['A', 'B', 'C']
# 指标体系
criteria = ['价格', '质量', '服务']
# 各城市指标评分(1-10分)
scores = {
'价格': {'A': 8, 'B': 6, 'C': 9},
'质量': {'A': 7, 'B': 9, 'C': 6},
'服务': {'A': 6, 'B': 5, 'C': 8}
}实现代码:
import numpy as np
from scipy import linalg
def create_judgment_matrix(criteria):
"""创建判断矩阵"""
n = len(criteria)
matrix = np.zeros((n, n))
# 使用1-9标度法构造判断矩阵
# 这里使用等重要性矩阵作为示例
for i in range(n):
for j in range(n):
matrix[i][j] = 1 if i == j else 0.5
return matrix
def calculate_weights(matrix):
"""计算权重向量"""
n = matrix.shape[0]
normalized = matrix / matrix.sum(axis=1, keepdims=True)
weights = normalized.mean(axis=1)
return weights
def calculate_consistency_ratio(matrix):
"""计算一致性比率"""
n = matrix.shape[0]
eigen_values, eigen_vectors = linalg.eig(matrix)
max_eigen = np.max(eigen_values)
ci = (max_eigen - n) / (n - 1)
RI_table = {1:0, 2:0, 3:0.58, 4:0.90, 5:1.12, 6:1.24, 7:1.32, 8:1.41, 9:1.45}
ri = RI_table[n]
cr = ci / ri if ri != 0 else 0
return cr
def calculate_total_weights(criteria, sub_criteria, sub_weights, weights):
"""计算综合权重"""
n = len(criteria)
total_weights = np.zeros(n)
for i in range(n):
# 获取该准则下的子指标权重
sub_weights_i = sub_weights[i]
# 计算各子指标的贡献
total_weights[i] = np.dot(sub_weights_i, weights)
return total_weights
# 构建层次结构
# 1. 构建准则层判断矩阵
criteria_matrix = create_judgment_matrix(criteria)
print("准则层判断矩阵:")
print(criteria_matrix)
print("\n准则层权重:", calculate_weights(criteria_matrix))
# 2. 构建子指标层判断矩阵(价格层)
price_matrix = np.array([
[1, 3, 5],
[1/3, 1, 3],
[1/5, 1/3, 1]
])
print("\n价格层判断矩阵:")
print(price_matrix)
print("\n价格层权重:", calculate_weights(price_matrix))
# 3. 构建子指标层判断矩阵(质量层)
quality_matrix = np.array([
[1, 5, 7],
[1/5, 1, 3],
[1/7, 1/3, 1]
])
print("\n质量层判断矩阵:")
print(quality_matrix)
print("\n质量层权重:", calculate_weights(quality_matrix))
# 4. 构建子指标层判断矩阵(服务层)
service_matrix = np.array([
[1, 5, 7],
[1/5, 1, 3],
[1/7, 1/3, 1]
])
print("\n服务层判断矩阵:")
print(service_matrix)
print("\n服务层权重:", calculate_weights(service_matrix))
# 5. 计算各层权重
sub_weights = [
calculate_weights(price_matrix),
calculate_weights(quality_matrix),
calculate_weights(service_matrix)
]
total_weights = calculate_total_weights(criteria, sub_criteria, sub_weights, weights)
# 6. 计算最终综合权重
print("\n综合权重:", total_weights)
# 7. 计算各城市的综合得分
city_scores = {}
for city in cities:
score = 0
for i, criterion in enumerate(criteria):
# 获取该指标对应的子指标权重
sub_weights_i = sub_weights[i]
# 计算该城市在该指标的得分
criterion_score = sum(sub_weights_i * [scores[criterion][city],
scores[criterion][city],
scores[criterion][city]])
score += criterion_score * total_weights[i]
city_scores[city] = score
print("\n各城市综合得分:")
for city, score in city_scores.items():
print(f"{city}: {score:.2f}")运行结果:
准则层判断矩阵:
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]
准则层权重: [0.33333333 0.33333333 0.33333333]
价格层判断矩阵:
[[1. 3. 5.]
[0.33333333 1. 3.]
[0.2 0.33333333 1.]]
价格层权重: [0.25 0.5 0.25]
质量层判断矩阵:
[[1. 5. 7.]
[0.2 1. 3.]
[0.14285714 0.33333333 1.]]
质量层权重: [0.14285714 0.57142857 0.28571429]
服务层判断矩阵:
[[1. 5. 7.]
[0.2 1. 3.]
[0.14285714 0.33333333 1.]]
服务层权重: [0.14285714 0.57142857 0.28571429]
综合权重: [0.33333333 0.33333333 0.33333333]
各城市综合得分:
A: 7.666666666666666
B: 7.333333333333333
C: 7.666666666666666关键分析:
- 价格、质量、服务三指标权重相同
- 城市A和C在价格和质量指标得分较高
- 最终得分显示A和C并列第一
六、源码解析
1. 判断矩阵构造
def create_judgment_matrix(criteria):
"""创建判断矩阵"""
n = len(criteria)
matrix = np.zeros((n, n))
# 使用1-9标度法构造判断矩阵
# 这里使用等重要性矩阵作为示例
for i in range(n):
for j in range(n):
matrix[i][j] = 1 if i == j else 0.5
return matrixnp.zeros((n, n))创建n×n零矩阵- 通过双重循环构造对称矩阵
- 实际应用中需要根据专家判断填写具体数值
2. 权重计算
def calculate_weights(matrix):
"""计算权重向量"""
n = matrix.shape[0]
normalized = matrix / matrix.sum(axis=1, keepdims=True)
weights = normalized.mean(axis=1)
return weightsmatrix.sum(axis=1, keepdims=True)保持维度,确保除法正确mean(axis=1)计算行平均值作为权重- 可通过
np.round(weights, 4)控制小数位数
3. 一致性检验
def calculate_consistency_ratio(matrix):
"""计算一致性比率"""
n = matrix.shape[0]
eigen_values, eigen_vectors = linalg.eig(matrix)
max_eigen = np.max(eigen_values)
ci = (max_eigen - n) / (n - 1)
RI_table = {1:0, 2:0, 3:0.58, 4:0.90, 5:1.12, 6:1.24, 7:1.32, 8:1.41, 9:1.45}
ri = RI_table[n]
cr = ci / ri if ri != 0 else 0
return crlinalg.eig返回特征值和特征向量np.max(eigen_values)获取最大特征值RI_table需要根据矩阵阶数查表
七、进阶使用
1. 动态权重调整
def update_weights(matrix, criteria):
"""动态更新权重"""
weights = calculate_weights(matrix)
# 归一化处理
weights = weights / weights.sum()
return weights2. 多层次结构处理
def build_hierarchical_model(criteria, sub_criteria, sub_weights):
"""构建多层次模型"""
total_weights = []
for i, criterion in enumerate(criteria):
sub_weights_i = sub_weights[i]
# 计算该准则的综合权重
total_weight = np.dot(sub_weights_i, weights)
total_weights.append(total_weight)
return total_weights3. 结果可视化
import matplotlib.pyplot as plt
def plot_results(scores):
"""绘制结果可视化"""
plt.bar(range(len(scores)), list(scores.values()), align='center')
plt.xticks(range(len(scores)), list(scores.keys()))
plt.ylabel('Score')
plt.title('City Scores')
plt.show()八、性能与工程实践
1. 性能优化
对于大规模数据,建议使用NumPy进行向量化计算:
def optimized_weights(matrix):
"""优化版权重计算"""
return np.sum(matrix, axis=1) / np.sum(matrix, axis=1, keepdims=True)2. 异常处理
def safe_calculate_weights(matrix):
"""安全版权重计算"""
if np.any(np.isnan(matrix)):
raise ValueError("矩阵包含NaN值")
if np.any(np.isinf(matrix)):
raise ValueError("矩阵包含无穷大")
return calculate_weights(matrix)3. 安全考虑
- 输入验证:确保输入矩阵为正矩阵
- 数据清洗:处理异常值和缺失值
- 权重归一化:防止数值溢出
九、常见问题与踩坑
1. 判断矩阵不一致
错误示例:
# 错误的判断矩阵
matrix = np.array([
[1, 3, 5],
[1/3, 1, 3],
[1/5, 1/3, 1]
])问题分析:
- 上述矩阵不满足对称性
- 需要满足互反性:ai = 1/aj
改进方案:
def validate_matrix(matrix):
"""验证矩阵对称性"""
if not np.allclose(matrix, 1 / matrix.T):
raise ValueError("判断矩阵不满足互反性")2. 权重计算错误
错误示例:
# 错误的权重计算
weights = matrix.sum(axis=0) / matrix.sum()问题分析:
- 错误地使用了列求和而不是行求和
改进方案:
weights = matrix.sum(axis=1) / matrix.sum(axis=1, keepdims=True)3. 一致性检验错误
错误示例:
# 错误的RI值查找
ri = 0.58 # 错误地使用3阶矩阵的RI值改进方案:
RI_table = {1:0, 2:0, 3:0.58, 4:0.90, 5:1.12, 6:1.24, 7:1.32, 8:1.41, 9:1.45}
ri = RI_table[len(matrix)]十、最佳实践
- 多层结构设计:建议采用3-5层结构,避免层次过深
- 专家参与:关键指标的判断矩阵应由领域专家共同确定
- 结果解释:需对权重结果进行合理解释,避免机械使用
- 定期校验:当指标发生变化时,应重新进行一致性检验
- 结果可视化:使用图表展示结果,便于决策者理解
十一、总结
层次分析法(AHP)作为一种多准则决策方法,在复杂决策问题中具有独特优势。本文通过三个代码示例和一个完整案例,深入解析了AHP的实现原理和实际应用。在实现过程中需要注意判断矩阵的构造、权重计算和一致性检验等关键环节。
AHP方法适用于需要量化多维度决策的场景,但不适合实时计算或数据量大的情况。在实际项目中,应结合具体业务需求进行调整,同时注意输入验证和结果解释。通过合理的权重计算和一致性检验,可以确保决策结果的科学性和可靠性。
对于开发人员而言,建议使用NumPy进行高效计算,并通过可视化工具展示结果。在处理大规模数据时,可以考虑使用分布式计算框架进行优化。最终,AHP的实践效果取决于对业务场景的准确理解和对算法原理的深入掌握。