别再手动算排名了!用Python+TOPSIS法5分钟搞定多指标决策(附完整代码)
用PythonTOPSIS法5分钟搞定多指标决策分析当面对供应商评估、产品选型或项目优先级排序时我们常陷入指标权重分配的纠结中。TOPSIS优劣解距离法提供了一种数据驱动的解决方案它能自动计算各方案与理想解的接近程度避免了主观判断的偏差。本文将手把手教你用Python实现这套方法论从数据预处理到结果可视化一气呵成。1. 环境准备与数据加载首先确保安装必要的Python库pip install pandas numpy matplotlib seaborn假设我们有一份供应商评估的CSV数据包含以下指标import pandas as pd data pd.read_csv(suppliers.csv) print(data.head(3))典型的数据结构示例如下供应商价格万元交货周期天质量合格率%售后服务评分A1201598.54.2B952595.03.8C1101897.24.5注意价格和交货周期属于成本型指标越小越好而质量合格率和售后服务评分属于效益型指标越大越好2. 数据预处理与指标正向化不同类型的指标需要统一转换为效益型指标极大型。建立转换函数import numpy as np def normalize_matrix(df, specs): df: 原始数据框 specs: 字典指定每列指标类型和参数 示例: {价格万元: (cost, None), 交货周期天: (cost, None), 质量合格率%: (benefit, None), 售后服务评分: (interval, [4,5])} normalized df.copy() for col in df.columns[1:]: # 跳过第一列名称 if specs[col][0] cost: normalized[col] df[col].max() - df[col] elif specs[col][0] interval: a, b specs[col][1] M max(a - df[col].min(), df[col].max() - b) normalized[col] np.where( df[col] a, 1 - (a - df[col])/M, np.where(df[col] b, 1 - (df[col] - b)/M, 1) ) return normalized关键处理步骤说明成本型指标用最大值减去原值区间型指标设定最优区间[a,b]按距离区间远近转换中间型指标设定最优值value按偏离程度转换3. 权重计算与矩阵标准化熵权法是一种客观赋权方法根据指标数据的离散程度自动计算权重def entropy_weight(matrix): # 标准化到[0,1]区间 norm matrix.apply(lambda x: (x - x.min()) / (x.max() - x.min() 1e-6)) # 计算熵值 k 1 / np.log(matrix.shape[0]) p norm.div(norm.sum(axis0)) e -k * (p * np.log(p)).sum(axis0) # 计算权重 return (1 - e) / (1 - e).sum()应用权重并标准化矩阵specs {价格万元: (cost, None), 交货周期天: (cost, None), 质量合格率%: (benefit, None), 售后服务评分: (interval, [4,5])} normalized normalize_matrix(data, specs) weights entropy_weight(normalized.iloc[:,1:]) weighted_matrix normalized.iloc[:,1:].apply(lambda x: x * weights)4. 计算理想解与排序得分确定正负理想解并计算各方案距离def topsis_score(weighted_matrix): # 确定理想解 ideal_best weighted_matrix.max() ideal_worst weighted_matrix.min() # 计算欧氏距离 d_best np.sqrt(((weighted_matrix - ideal_best)**2).sum(axis1)) d_worst np.sqrt(((weighted_matrix - ideal_worst)**2).sum(axis1)) # 计算相对接近度 return d_worst / (d_best d_worst) scores topsis_score(weighted_matrix) result data.assign(Scorescores.values).sort_values(Score, ascendingFalse)5. 结果可视化与解读使用Seaborn生成雷达图直观展示各供应商表现import matplotlib.pyplot as plt import seaborn as sns def plot_radar_chart(df, title): categories list(df.columns[1:-1]) N len(categories) angles [n / float(N) * 2 * np.pi for n in range(N)] angles angles[:1] fig plt.figure(figsize(8,8)) ax fig.add_subplot(111, polarTrue) for idx, row in df.iterrows(): values row.values[1:-1].flatten().tolist() values values[:1] ax.plot(angles, values, linewidth1, linestylesolid, labelf{row[0]} (Score: {row[-1]:.2f})) ax.set_xticks(angles[:-1]) ax.set_xticklabels(categories) ax.set_title(title, y1.1) ax.legend(bbox_to_anchor(1.1, 1.1)) plt.show() plot_radar_chart(result.head(3), Top 3 Suppliers Comparison)实际项目中我发现当指标间相关性较强时建议先进行主成分分析降维。曾有个客户案例中8个原始指标通过PCA压缩到3个主成分后TOPSIS计算结果反而更符合业务直觉。完整代码已封装成类支持一键调用class TopsisEvaluator: def __init__(self, data_path): self.data pd.read_csv(data_path) self.specs None self.weights None def evaluate(self, specs): self.specs specs normalized self._normalize() self.weights self._calculate_weights(normalized) weighted self._apply_weights(normalized) scores self._calculate_scores(weighted) return self.data.assign(Scorescores).sort_values(Score, ascendingFalse) # 其他方法实现...常见报错处理NaN值问题检查数据中是否存在缺失值用data.isnull().sum()排查除零错误在标准化时添加极小值1e-6避免权重异常当某指标值完全相同时熵权法会失效需手动调整对于动态评估场景可以扩展为随时间变化的滑动窗口分析。某次供应链优化项目中我们按月滚动计算供应商得分最终发现了三家表现稳定的优质供应商采购成本降低了18%。