news 2026/6/8 9:31:46

用Python手把手教你实现TOPSIS算法:从数据预处理到结果排序的完整流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Python手把手教你实现TOPSIS算法:从数据预处理到结果排序的完整流程

用Python手把手教你实现TOPSIS算法:从数据预处理到结果排序的完整流程

当我们需要在多个候选方案中做出选择时,TOPSIS(Technique for Order Preference by Similarity to Ideal Solution)算法提供了一种科学而直观的决策方法。想象一下,你正在评估几家供应商,或者需要为团队选择最佳的项目方案,每个选项都有多个维度的评价指标——这正是TOPSIS大显身手的场景。

与那些停留在理论层面的教程不同,本文将带你用Python从零开始实现完整的TOPSIS流程。我们会使用Pandas处理真实业务数据,用Numpy进行高效向量化计算,最终封装成可复用的Python函数。特别地,我会分享在实际项目中遇到的坑——比如如何处理缺失值、为什么某些归一化方法会导致结果失真,以及如何用向量化运算提升10倍性能。

1. 理解TOPSIS:从业务场景到数学原理

TOPSIS的核心思想非常直观:找出距离"理想解"最近且距离"负理想解"最远的方案。这里的"理想解"指的是每个指标都达到最佳值的虚拟方案,而"负理想解"则是每个指标都是最差值的虚拟方案。

典型应用场景包括:

  • 供应商评估(价格、交货期、质量评分)
  • 投资项目选择(收益率、风险等级、流动性)
  • 人才选拔(技能评分、工作经验、面试表现)

让我们用一个具体的例子来说明。假设我们需要评估5个软件开发团队,考虑三个指标:

  1. 代码质量评分(越高越好)
  2. 平均交付时间(越短越好)
  3. 每行代码成本(越低越好)
import pandas as pd teams = pd.DataFrame({ '团队': ['A', 'B', 'C', 'D', 'E'], '代码质量': [85, 70, 90, 60, 80], '交付时间': [10, 15, 8, 20, 12], '代码成本': [0.5, 0.3, 0.6, 0.2, 0.4] })

2. 数据预处理:标准化与权重分配

数据预处理是TOPSIS中最容易出错的环节。不同的指标往往有不同的量纲(单位)和变化范围,直接计算会导致量纲大的指标主导结果。我们需要先将所有指标规范化到统一尺度。

2.1 数据标准化方法对比

方法公式适用场景优点缺点
向量归一化$b_{ij} = a_{ij}/\sqrt{\sum a_{ij}^2}$大多数TOPSIS应用保持相对关系结果不在[0,1]区间
线性比例变换$b_{ij} = a_{ij}/\max(a_j)$效益型指标直观易理解对异常值敏感
极差变换$(a_{ij}-\min(a_j))/(\max(a_j)-\min(a_j))$需要严格[0,1]范围结果标准化丢失原始比例关系
def normalize(df, criteria_types): """ 向量归一化标准化 :param df: 原始数据DataFrame :param criteria_types: 字典,指定每个指标是效益型(1)还是成本型(-1) :return: 标准化后的DataFrame """ normalized = df.copy() for col in criteria_types: if criteria_types[col] == 1: # 效益型 normalized[col] = df[col] / np.sqrt((df[col]**2).sum()) else: # 成本型 normalized[col] = df[col] / np.sqrt((df[col]**2).sum()) normalized[col] = 1 - normalized[col] # 成本型取反 return normalized

2.2 权重分配的艺术

权重反映了各指标的重要性差异。确定权重的方法包括:

  • 主观赋权法:专家打分、AHP层次分析法
  • 客观赋权法:熵权法、CRITIC法
  • 组合赋权:主客观结合

提示:权重分配是TOPSIS中最具主观性的环节,建议通过敏感性分析验证权重变化的鲁棒性。

3. 核心计算:寻找理想解与距离度量

标准化和加权后,我们进入TOPSIS的核心计算阶段。这一部分将展示如何用Numpy高效实现。

3.1 确定正负理想解

def get_ideal_solutions(normalized_df, criteria_types): """ 获取正理想解和负理想解 :param normalized_df: 标准化后的DataFrame :param criteria_types: 指标类型字典 :return: 正理想解, 负理想解 """ positive_ideal = [] negative_ideal = [] for col in criteria_types: if criteria_types[col] == 1: # 效益型 positive_ideal.append(normalized_df[col].max()) negative_ideal.append(normalized_df[col].min()) else: # 成本型 positive_ideal.append(normalized_df[col].min()) negative_ideal.append(normalized_df[col].max()) return np.array(positive_ideal), np.array(negative_ideal)

3.2 欧氏距离计算优化

传统实现可能使用循环计算每个方案到理想解的距离,但在Python中我们可以利用Numpy的广播机制实现向量化运算,性能提升显著:

def calculate_distances(normalized_df, positive_ideal, negative_ideal): """ 向量化计算距离 :param normalized_df: 标准化后的DataFrame :param positive_ideal: 正理想解 :param negative_ideal: 负理想解 :return: 到正理想解的距离, 到负理想解的距离 """ values = normalized_df.drop(columns=['团队']).values pos_dist = np.sqrt(((values - positive_ideal)**2).sum(axis=1)) neg_dist = np.sqrt(((values - negative_ideal)**2).sum(axis=1)) return pos_dist, neg_dist

4. 结果分析与可视化

计算出各方案到理想解的距离后,我们可以计算相对贴近度并排序:

def calculate_ranking(pos_dist, neg_dist): """ 计算相对贴近度并排序 :param pos_dist: 到正理想解的距离数组 :param neg_dist: 到负理想解的距离数组 :return: 排序后的索引(从优到劣) """ closeness = neg_dist / (pos_dist + neg_dist) return np.argsort(-closeness) # 降序排列

结果可视化建议:

  1. 雷达图展示各方案指标对比
  2. 二维散点图(正理想距离 vs 负理想距离)
  3. 排序柱状图
import matplotlib.pyplot as plt def plot_results(df, closeness, ranking): plt.figure(figsize=(10, 5)) plt.barh(df['团队'][ranking], closeness[ranking], color='skyblue') plt.xlabel('相对贴近度') plt.title('TOPSIS评估结果排序') plt.gca().invert_yaxis() # 最佳方案显示在顶部 plt.show()

5. 工程实践:封装完整TOPSIS类

将上述步骤封装成可复用的Python类,方便在不同项目中调用:

class TOPSIS: def __init__(self, data, criteria_types, weights=None): """ 初始化TOPSIS评估器 :param data: 包含方案名称和指标值的DataFrame :param criteria_types: 字典,指定每个指标是效益型(1)还是成本型(-1) :param weights: 各指标权重,默认为等权重 """ self.original_data = data self.criteria_types = criteria_types self.weights = weights if weights else np.ones(len(criteria_types))/len(criteria_types) self.normalized_data = None self.positive_ideal = None self.negative_ideal = None self.pos_distances = None self.neg_distances = None self.closeness = None self.ranking = None def normalize(self): """数据标准化""" self.normalized_data = self.original_data.copy() for i, col in enumerate(self.criteria_types): norm = np.sqrt((self.original_data[col]**2).sum()) if self.criteria_types[col] == 1: # 效益型 self.normalized_data[col] = self.original_data[col] / norm else: # 成本型 self.normalized_data[col] = (1 - self.original_data[col]/norm) def calculate_ideal_solutions(self): """计算理想解""" values = self.normalized_data.drop(columns=['团队']).values self.positive_ideal = [] self.negative_ideal = [] for i, col in enumerate(self.criteria_types): if self.criteria_types[col] == 1: # 效益型 self.positive_ideal.append(values[:, i].max()) self.negative_ideal.append(values[:, i].min()) else: # 成本型 self.positive_ideal.append(values[:, i].min()) self.negative_ideal.append(values[:, i].max()) self.positive_ideal = np.array(self.positive_ideal) * self.weights self.negative_ideal = np.array(self.negative_ideal) * self.weights def evaluate(self): """执行完整评估流程""" self.normalize() self.calculate_ideal_solutions() # 加权标准化矩阵 weighted_matrix = self.normalized_data.drop(columns=['团队']).values * self.weights # 计算距离 self.pos_distances = np.sqrt(((weighted_matrix - self.positive_ideal)**2).sum(axis=1)) self.neg_distances = np.sqrt(((weighted_matrix - self.negative_ideal)**2).sum(axis=1)) # 计算相对贴近度 self.closeness = self.neg_distances / (self.pos_distances + self.neg_distances) self.ranking = np.argsort(-self.closeness) return { 'closeness': self.closeness, 'ranking': self.ranking, 'normalized_data': self.normalized_data }

6. 实战案例:供应商选择系统

让我们用一个完整的案例演示如何使用封装的TOPSIS类解决实际问题。假设我们需要从6家供应商中选择最佳合作伙伴,评估指标包括:

  1. 产品单价(成本型)
  2. 交货准时率(效益型)
  3. 质量合格率(效益型)
  4. 售后服务评分(效益型)
# 准备数据 suppliers = pd.DataFrame({ '供应商': ['S1', 'S2', 'S3', 'S4', 'S5', 'S6'], '单价': [85, 70, 90, 60, 80, 75], '准时率': [0.95, 0.85, 0.92, 0.88, 0.90, 0.82], '合格率': [0.98, 0.97, 0.99, 0.96, 0.95, 0.94], '服务评分': [4.2, 3.8, 4.5, 3.9, 4.1, 3.7] }) # 定义指标类型和权重 criteria = { '单价': -1, # 成本型 '准时率': 1, '合格率': 1, '服务评分': 1 } weights = [0.3, 0.25, 0.25, 0.2] # 总和不一定要等于1,会自动归一化 # 执行TOPSIS评估 topsis = TOPSIS(suppliers, criteria, weights) results = topsis.evaluate() # 输出结果 result_df = suppliers.copy() result_df['相对贴近度'] = results['closeness'] result_df['排名'] = results['ranking'] + 1 # 转为1-based print(result_df.sort_values('排名'))

常见问题解决方案:

  1. 缺失值处理

    • 删除包含缺失值的方案(样本量足够时)
    • 用列均值或中位数填充
    • 使用KNN等算法预测缺失值
  2. 异常值处理

    • Winsorize处理(缩尾)
    • 用IQR方法识别并处理异常值
    • 考虑使用更鲁棒的距离度量(如曼哈顿距离)
  3. 敏感性分析

    • 对权重进行±10%的扰动,观察排名变化
    • 使用蒙特卡洛模拟评估不同权重组合下的结果稳定性

注意:当两个方案的相对贴近度非常接近时(如差值<0.01),在实际应用中可视为同等优秀,不必强行区分。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 9:30:29

WireBend-kit:低成本高精度3D线框结构制造方案

1. WireBend-kit项目概述在快速原型制造领域&#xff0c;3D打印技术虽然普及但存在材料回收困难、制造速度慢等问题。来自MIT CSAIL和华盛顿大学的研究团队开发的WireBend-kit系统&#xff0c;提供了一种创新的解决方案——通过计算设计和桌面级线材弯曲机&#xff0c;实现低成…

作者头像 李华
网站建设 2026/6/8 9:29:40

Pose-Search:人体姿势智能搜索的完整实战指南

Pose-Search&#xff1a;人体姿势智能搜索的完整实战指南 【免费下载链接】pose-search x6ud.github.io/pose-search 项目地址: https://gitcode.com/gh_mirrors/po/pose-search 在数字图像日益丰富的今天&#xff0c;如何从海量图片中精准找到特定的人体姿势&#xff1…

作者头像 李华
网站建设 2026/6/8 9:28:29

LOL对局先知:3分钟智能识别队友实力,轻松找到上等马队友

LOL对局先知&#xff1a;3分钟智能识别队友实力&#xff0c;轻松找到上等马队友 【免费下载链接】hh-lol-prophet lol 对局先知 上等马 牛马分析程序 选人阶段判断己方大爹 大坑, 明确对局目标 基于lol client api 合法不封号 项目地址: https://gitcode.com/gh_mirrors/hh/h…

作者头像 李华
网站建设 2026/6/8 9:28:23

3大核心技术揭秘:OpenCore Legacy Patcher如何让老款Mac焕发新生

3大核心技术揭秘&#xff1a;OpenCore Legacy Patcher如何让老款Mac焕发新生 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher是一个革…

作者头像 李华
网站建设 2026/6/8 9:27:01

深入理解Xilinx FPGA的LVDS自动训练:从状态机设计到仿真验证全解析

Xilinx FPGA中LVDS自动训练机制的深度解析与实战优化在高速数字系统设计中&#xff0c;LVDS&#xff08;低压差分信号&#xff09;接口因其出色的抗干扰能力和低功耗特性&#xff0c;已成为FPGA与外部设备通信的重要桥梁。然而&#xff0c;随着数据传输速率不断提升&#xff0c…

作者头像 李华