☰
CRITIC法详解:从原理到Python实现,科学计算指标权重
2026/9/26 20:12:21 网站建设 项目流程

上个月做供应商评估的时候,又双叒被“权重从哪来”难住了:6个一级指标、12家备选供应商,数据都是现成的,可请专家打分凑不齐人,就算凑齐了,十个人打出来的权重也未必统一,业务方还会追问“你凭什么给这个指标0.2”。后来我把之前用过几次的CRITIC法捡起来,算了一版指标权重,再用几张图把计算依据和结果一起展示出来,管理层十分钟就看懂了。这篇文章就是那次完整复盘,把CRITIC法的原理、手算过程、Python实现和绘图方法串起来讲一遍。CRITIC法适合那种“有客观数据、不方便做主观打分”的评价场景,比如供应商评分、城市发展水平评估、招聘候选人筛选、实验室能力考核,给指标算出一个可解释、可复现的客观权重。想直接拿代码的可以跳到第3节,想弄懂底层逻辑的按顺序往下读就行。

1. 为什么选CRITIC:多指标评价里的权重困境

1.1 主观赋权的痛,客观赋权的快

做过多指标综合评价的人都知道,权重是整个评价模型里最敏感、也最容易吵架的部分。最传统的方式是专家打分或层次分析法,请几个有经验的人坐在一起,两两比较重要性,最后算出一组权重。这套思路业务上认,但实际操作很痛苦:一是专家时间难约,十个人能凑齐五个就不错;二是主观判断不一致,同一对指标有人觉得A比B重要两倍,有人觉得反过来;三是结果很难被复检,半年后再请同一批专家打分,权重大概率会漂移。当评价对象很多、指标很多时,主观赋权的时间成本、沟通成本都会被放大。

客观赋权法解决的就是这个痛点:完全从数据本身出发,不需要任何人拍脑袋。数据里哪个指标携带的信息多,哪个指标在评价对象之间区分度大,它就值得更大的权重。常见客观赋权法里,除了CRITIC,还有熵权法、变异系数法、主成分分析赋权等。CRITIC这个名字来自Criteria Importance Through Intercriteria Correlation,直译就是“通过指标间相关性判断指标重要性”,它的一大好处是把“指标自身的波动”和“指标之间的信息重复度”都纳入计算,逻辑上比只看单维度的熵权法、变异系数法更完整。

1.2 CRITIC的核心逻辑:一个指标的信息量要看“反差+冲突”

CRITIC的基本思想可以用一句话概括:一个指标越“能打”,权重就应该越高。“能打”体现在两个维度。

第一个维度叫对比强度,通常用标准差表示。标准差大,说明不同评价对象在这个指标上的差异大,该指标能把人区分开;如果大家得分都差不多,比如都在95到96分之间,那这个指标对最终排名的贡献自然很小。你可以把它理解成一个筛子,筛孔越小,能筛出来的差异越少,信息量越低。

第二个维度叫冲突性,用的是指标之间的相关系数。这个思路很巧妙:如果两个指标高度正相关,比如“营业收入”和“营业成本”基本同步变化,那么它们在评价时提供的信息严重重复,等于一个人说了两遍相同的话,第二遍就没多少新意;反过来,如果两个指标相关性很弱,或者干脆负相关,那每个指标都在贡献独立的信息,各自的权重就该往上走。CRITIC把冲突性定义为指标与其余所有指标相关系数的距离之和,相关系数越低,冲突性越大,指标越不可替代。

最后把对比强度和冲突性乘起来,得到每个指标的综合信息量,再做归一化,就是最终权重。这个乘法的含义很直观:既要在单个指标上波动明显,又要和其他指标不重复,两者缺一不可。

1.3 和熵权法、变异系数法放一起比一比

我一直觉得,选方法之前一定要知道其他方法在干什么。熵权法用信息熵来衡量指标的离散程度,熵越小说明指标携带的信息量越大,权重越高;变异系数法则直接拿标准差除以均值,用相对波动大小来定权。这两个方法都只关注“指标自身的变化”,完全没管指标之间的相关性。

方法核心依据是否考虑指标间相关性适用场景局限
变异系数法标准差/均值否指标数量少、独立性较好忽略信息重叠
熵权法信息熵否数据离散度差异明显对指标分布敏感,忽略相关结构
CRITIC法标准差+相关系数是指标间可能存在关联的综合评价对相关系数稳定性有要求

举例来说,如果指标体系里有两个强相关的指标,熵权法和变异系数法会各给它们一个不小的权重,等于把“同一件事”算了两次,综合得分会被带偏。CRITIC能通过冲突性把冗余信息的权重压下来,所以在指标间关联度较高的场景下,我一般优先选CRITIC。当然它不是万能的,后面会讲到它对相关系数很敏感,样本太少时容易出问题。

2. CRITIC法计算全流程:每个中间量都是什么含义

2.1 指标正向化:先消除“方向”干扰

CRITIC计算权重之前,必须先统一指标方向。这个步骤很多人会跳过,但跳过的后果很严重。我们平时遇到的指标大约分三类:正向指标越大越好,比如收入、得分、覆盖率;负向指标越小越好,比如成本、能耗、投诉率;还有适度指标,比如温度、pH值,落在某个区间最好。

如果不做方向统一,直接拿原始数据算相关系数,就会出现一种尴尬情况:某个负向指标和另一个正向指标在业务上是此消彼长的,相关系数为负,但这个负相关可能只是“方向相反”造成的,并不代表两个指标真的在互相补充。方向统一之后,所有指标都变成“越大越好”,相关系数的符号才更有解释意义。

负向指标的常用正向化变换是取补数:x' = (max(x) - x) / (max(x) - min(x))。这样最小值变成0,最大值变成1,方向和正向指标一致了。适度指标的正向化稍微麻烦,常见做法是先构造离最优值的距离,比如x' = 1 / (1 + |x - x_opt|),距离越小得分越高。如果一套指标体系里方向很杂,最好先写一个映射表,明确每个指标的类型,再批量处理,别在代码里一个个改,容易漏。

2.2 极差标准化:把数据压到同一个尺度

方向统一之后,还要做无量纲化。CRITIC的标准差计算要求指标在同一量纲下进行,否则“人均GDP”这种数值大的指标标准差天然巨大,“万元产值能耗”这种数值小的指标标准差天然很小,最后权重会被量纲带偏,完全跑偏。

极差标准化是最常用的方案:正向指标用(x - min) / (max - min),把数据映射到[0, 1]区间;负向指标在2.1步已经通过取补数变成正向,同样用这个公式。这一步之后,所有指标的最小值是0、最大值是1,量纲影响消除了,而且数据的相对排序不变。

有个细节容易被忽略:标准化用的是整个评价对象集合的min和max,不是单个对象。比如我们有12家供应商的数据,计算某家供应商的标准化值时,分母必须是这12家供应商里该指标的最大最小值,不能只拿自己的历史数据算,否则不同批次的标准化结果不可比。

2.3 对比强度:标准差到底在表达什么

在CRITIC法里,对比强度一般取标准化后数据的标准差。标准差大,表示指标在各个评价对象之间差异显著,辨识度高,能给最终排名提供更多信息;标准差小,表示大家在这个指标上都差不多,拉不开差距,权重就该小。

这里必须提醒一个容易踩的坑:标准差有总体标准差和样本标准差之分。pandas里的std()默认是样本标准差,分母是n-1;而评价对象通常就是我们的全量集合,不是从总体里随机抽的样本,所以理论上用总体标准差更符合CRITIC的原始设定,也就是ddof=0。两种算法在样本量大的时候差别很小,但在只有4个、5个评价对象的时候,权重结果会出现肉眼可见的差异。章节4里我会单独演示这个差异有多大。

2.4 冲突性:相关系数如何变成“不重复程度”

冲突性是CRITIC的第二块拼图。先计算标准化后指标间的皮尔逊相关系数矩阵,得到两两指标的相关程度,然后用公式把相关系数转成冲突性。

对于某一个指标j,冲突性:R_j = Σ(1 - r_jk),其中k是除j以外的所有指标,r_jk是j和k的相关系数。相关系数越接近1,1 - r_jk越小,说明两个指标信息高度重叠,冲突性低;相关系数越接近0或者为负,1 - r_jk越大,冲突性高。把指标j和所有其他指标的冲突值加总,就是它在这个指标体系里的总体不可替代程度。

这个公式有个值得商榷的地方:1 - r在遇到负相关时会变得很大,比如r=-0.8时冲突值是1.8,而r=0.3时只有0.7。也就是说,两个指标越是反向变化,冲突性越高。这在“评价信息互补”的语境下是合理的,但有些研究者会觉得,负相关其实也是一种强关联,既然关系强,信息冗余度也应该高,所以主张用1 - |r|来算。两种思路都有道理,后面会说我的处理习惯。

2.5 信息量合成与权重归一化

拿到对比强度σ_j和冲突性R_j之后,合成信息量:C_j = σ_j × R_j。这个乘积就是指标j在评价体系里的总信息量,然后做归一化:w_j = C_j / ΣC_j,得到最终权重。

整个流程跑下来,你会发现CRITIC其实没有特别玄学的数学推导,每一步都有业务解释:标准差大说明指标“有区分度”,相关系数低说明指标“不重复”,两者都满足的指标自然是评价模型里最重要的。这种可解释性在向领导汇报时非常有用,比一句“算法算出来的”强得多。

2.6 手算示例:4个城市、3个指标,一步步算给你看

光讲公式还是抽象,我拿一个极简例子手算一遍。假设评价4个城市的发展水平,选3个指标:人均GDP(万元,正向)、空气优良天数比率(%,正向)、单位GDP能耗(吨标准煤/万元,负向)。原始数据如下:

城市人均GDP优良天数比率单位GDP能耗
城市112.878.50.92
城市29.685.30.65
城市315.272.11.10
城市411.590.00.78

先正向化并做极差标准化,单位GDP能耗取补数处理,得到:

城市人均GDP优良天数比率单位GDP能耗(正向化)
城市10.5710.3580.400
城市20.0000.7371.000
城市31.0000.0000.000
城市40.3391.0000.711

然后计算每个指标的总体标准差,以及两两之间的相关系数。相关系数矩阵大概是:人均GDP和优良天数比率约-0.83,人均GDP和单位GDP能耗约-0.99,优良天数比率和单位GDP能耗约0.86。也就是说,人均GDP和另外两个指标都是明显负相关,冲突性会非常高。

指标对比强度(标准差)冲突性信息量C权重
人均GDP0.3643.831.390.47
优良天数比率0.3791.970.750.26
单位GDP能耗0.3712.130.790.27

这个例子很典型:人均GDP虽然标准差不是最大的,但和另外两个指标都是强负相关,冲突性远高于其他指标,所以最后权重最大。如果我用的是一味强调“波动越大权越大”的变异系数法或熵权法,三个指标权重会相对平均,很难看出人均GDP的独特价值。这就是CRITIC法在多指标评价里最值得用的地方。

3. Python实现:权重计算与四张图一次搞定

3.1 最简核心代码:20行搞定权重

直接用pandas和numpy就行,不需要额外装复杂依赖。我把上面手算的例子写成完整代码,注释写在关键行。

import pandas as pd import numpy as np raw = pd.DataFrame({ '人均GDP': [12.8, 9.6, 15.2, 11.5], '优良天数比率': [78.5, 85.3, 72.1, 90.0], '单位GDP能耗': [0.92, 0.65, 1.10, 0.78] }) def minmax_positive(series): return (series - series.min()) / (series.max() - series.min()) def minmax_negative(series): # 负向指标先取补数,结果等于正向标准化 return (series.max() - series) / (series.max() - series.min()) df = pd.DataFrame({ '人均GDP': minmax_positive(raw['人均GDP']), '优良天数比率': minmax_positive(raw['优良天数比率']), '单位GDP能耗': minmax_negative(raw['单位GDP能耗']) }) std = df.std(axis=0, ddof=0) # 对比强度,用总体标准差 corr = df.corr() # 标准化后的相关系数矩阵 conflict = np.sum(1 - corr, axis=1) # 冲突性 info = std * conflict # 综合信息量 weights = info / info.sum() # 归一化权重 print("标准化数据:") print(df.round(3)) print("对比强度:") print(std.round(3)) print("相关系数矩阵:") print(corr.round(3)) print("冲突性:") print(conflict.round(3)) print("权重:") print(weights.round(4))

这段代码最大的优点是把每个中间量都单独拆出来打印了。我强烈建议你在自己的数据上也这样跑一遍,先检查中间量是否符合直觉,再决定要不要用结果,千万别直接跳到权重那一步。

3.2 权重条形图:一眼看出谁重要

权重算完,第一张图一定画水平条形图。竖向条形图在指标名比较长时会互相挤压,水平条形图最适合展示排名信息。

import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei'] plt.rcParams['axes.unicode_minus'] = False weights_sorted = weights.sort_values() fig, ax = plt.subplots(figsize=(8, 5)) colors = plt.cm.Blues(np.linspace(0.4, 0.9, len(weights_sorted))) bars = ax.barh(weights_sorted.index, weights_sorted.values, color=colors) ax.set_xlabel('权重') ax.set_title('CRITIC法计算的指标权重') for bar, value in zip(bars, weights_sorted.values): ax.text(bar.get_width() + 0.01, bar.get_y() + bar.get_height() / 2, f'{value:.3f}', va='center') plt.tight_layout() plt.show()

看这张图,第一个要确认的点是“权重排序是否符合业务直觉”。如果某个公认不重要的指标权重特别高,通常不是方法错了,而是数据本身有问题,比如该指标存在明显异常值,或者标准化逻辑写错了。

3.3 相关系数热力图:解释权重为什么这么分

权重图只能告诉大家结果,不能解释原因。CRITIC的权重有一半来自相关性,所以必须把相关系数矩阵画出来,汇报时才好说清楚。比如前面例子里人均GDP权重最高,就是因为它和优良天数比率、单位GDP能耗都是负相关,热力图往那一放,结论不言自明。

import seaborn as sns fig, ax = plt.subplots(figsize=(6, 5)) sns.heatmap(corr, annot=True, cmap='coolwarm', center=0, vmin=-1, vmax=1, fmt='.2f', xticklabels=corr.columns, yticklabels=corr.columns, ax=ax) ax.set_title('标准化指标间的相关系数') plt.tight_layout() plt.show()

热力图的配色我用的是coolwarm,以0为中心,正相关红色、负相关蓝色。vmin=-1, vmax=1必须写死,否则seaborn会按当前数据的最值自动缩放色带,比如实际相关系数范围是-0.9到0.8,色带就会把-0.9当成最深红,误导看图的人。

3.4 对比强度-冲突性气泡图:找到权重背后的驱动因素

权重是综合信息量归一化后的结果,可它不是中间变量,没法直观看出一个指标权重高,到底是因为标准差大,还是因为相关性低。气泡图能解决这个问题:横轴是对比强度,纵轴是冲突性,气泡大小是权重。哪个维度贡献大,一看便知。

fig, ax = plt.subplots(figsize=(8, 6)) scatter = ax.scatter(std.values, conflict.values, s=weights.values * 800, alpha=0.6, edgecolors='w') for name, x, y in zip(std.index, std.values, conflict.values): ax.annotate(name, (x, y), xytext=(6, 4), textcoords='offset points') ax.set_xlabel('对比强度(标准差)') ax.set_ylabel('冲突性') ax.set_title('对比强度与冲突性气泡图(气泡大小=权重)') plt.tight_layout() plt.show()

如果某指标在右上角,说明它同时具备“区分度大”和“信息独立”两种属性,是体系里的核心指标;如果某指标在左上方,说明冲突性高但自身波动小,权重大部分是靠和其他指标的低相关撑起来的,这种指标要留意后续数据更新后权重是否稳定。

3.5 雷达图:把三个维度压缩到一张图

最后这张雷达图更适合多指标场景,能把对比强度、冲突性、权重三个维度都放到一张图上对比。因为对比强度、冲突性和权重不在同一量纲,先做一次min-max归一化,再画雷达图。

from math import pi def normalize(series): return (series - series.min()) / (series.max() - series.min()) std_n = normalize(std) conflict_n = normalize(conflict) weights_n = normalize(weights) labels = df.columns.tolist() angles = [i / len(labels) * 2 * pi for i in range(len(labels))] angles += angles[:1] fig, ax = plt.subplots(figsize=(7, 7), subplot_kw={'projection': 'polar'}) for values, label in [(std_n.values, '对比强度'), (conflict_n.values, '冲突性'), (weights_n.values, '权重')]: data = list(values) + [values[0]] ax.plot(angles, data, label=label) ax.fill(angles, data, alpha=0.15) ax.set_xticks(angles[:-1]) ax.set_xticklabels(labels) ax.set_title('CRITIC三维特征雷达图') ax.legend(loc='upper right') plt.show()

雷达图的技巧是三个维度分开归一化,不然冲突性动辄接近2、对比强度只有0.36,画出来对比强度那条线会糊成一条直线,没有观察价值。归一化之后注意,图里只能看形状趋势,不能读绝对数值。

4. 常见问题与避坑记录

4.1 负相关系数会让权重扭曲吗

这是CRITIC法被问得最多的一个问题。原始公式1 - r在负相关时会放大冲突值,比如r=-0.9时冲突贡献1.9,而r=0.1时只有0.9,这等于把“反向变化”看得比“弱相关”还要珍稀。如果你的指标都是正向化之后的,负相关往往意味着指标之间存在某种此消彼长的业务关系,比如“成本控制”和“服务质量”天然就是负相关,放大冲突性是合理的。

但如果你不想让负相关被过度放大,可以用1 - |r|来计算冲突性。这时正相关0.9和负相关-0.9的冲突贡献都是0.1,核心思想变成“只要是强线性相关,信息冗余就大,不管同向还是反向”。我自己的习惯是两种版本都算一遍,看权重排序是否发生本质变化。如果排序很稳,说明结果对相关系数处理方式不敏感,可以直接用;如果排序剧烈波动,说明指标体系里存在强相关的核心变量,这时要回到业务层面重新审视指标设计,而不是纠结公式。

4.2 标准差用总体还是样本:结果差多少

前面提过pandas的std()默认ddof=1,是样本标准差,而CRITIC原始文献用的是总体标准差。评价对象一般是全量数据,不是抽样,所以用ddof=0更讲得通。拿前面的例子实际算一下:样本标准差下三个人均GDP的标准差是0.420,总体标准差是0.364;最终权重算出来差别很小,人均GDP还是0.47左右,因为所有指标的标准差都被同步放大了,归一化之后影响被部分抵消。

但这不是偷懒的理由。样本量只有四五个的时候,两种标准差的差异会更明显,而且一旦某个指标恰好有一两个极端值,标准差差异会被放大。我建议固定写ddof=0,并且注释里写明这是总体标准差,后面复算的人不会迷惑。

4.3 标准化方法不同,权重会翻车吗

极差标准化是CRITIC里最常用的预处理,但它对异常值很敏感:原始数据里突然冒出一个极大值,会直接把其他正常值压到0.2甚至更低,标准差和相关系数都会被带跑。如果你的数据存在明显离群点,优先考虑截断处理,比如把超过99%分位数的值截断到99%分位数的水平,再做极差标准化;或者改用z-score标准化,让数据围绕0波动,离群值影响会缓和一些。

注意,z-score标准化的结果存在负值,计算冲突性时不影响,但解释上不如[0,1]区间直观。标准化的选择会改变相关系数矩阵,进而改变权重,所以写分析报告时一定要写明预处理方案,否则结果不可复现。

4.4 样本量太小,相关系数不可靠

CRITIC对相关系数的依赖很高,而皮尔逊相关系数在小样本下波动剧烈。我个人的底线是评价对象数量至少大于指标数量,如果指标有6个,评价对象最好在30个以上;只有十几个样本时,算出来的相关矩阵方差很大,今天权重和明天权重可能完全两样。

如果样本量实在不够,可以做稳定性验证:用bootstrap抽样,每次从样本里重抽80%的对象计算权重,重复200次,看每个指标权重的分布范围。如果某个指标权重的置信区间横跨很多个位次,那它基本不能用于决策,需要在报告里明确说明。

4.5 中文绘图的三个老坑

中文字体问题是中文绘图里最常见的翻车现场。第一,不设置中文字体,图上全是方格,解决办法是在导入matplotlib之后立刻加上plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']。第二,负号显示异常,坐标轴上的负号会变成小方块,必须设置plt.rcParams['axes.unicode_minus'] = False。第三,字体家族名在不同系统里不一样,Windows常见宋体黑体,macOS上往往要换成'Arial Unicode MS'或'PingFang SC',建议写成列表,matplotlib会自动匹配第一个可用的字体。另外提醒一句,如果是在Jupyter里用seaborn画热力图,seaborn有自己的字体设置,最好在画图前也执行一遍同样的设置,别只设在matplotlib上。

4.6 权重结果怎么交付才不是一堆数字

最后这条是我自己的交付习惯。纯权重表格在业务侧很容易被挑战,我会额外做三件事:第一,把权重图和气泡图拼到一张A4报告页里,旁边标注每个指标的方向和业务含义;第二,用权重对原始数据加权计算综合得分,把排名结果和只用熵权法、变异系数法得到的排名做个对比,差异大的地方给出业务解释;第三,保留全部中间计算表,包括标准化数据、标准差、相关系数矩阵、冲突性、信息量,随时准备应对各种“为什么这个指标权重这么高”的追问。CRITIC的好处就在这里,每一步都有明确的业务解释,不需要黑盒。

我在实际项目中还发现一个特别好用的扩展:算完权重后,可以继续用k-means或者TOPSIS跑综合评价排名,把CRITIC的权重作为输入,这样权重计算和最终评价就串成了一条完整链路。只要前期数据清洗和标准化做得扎实,后面出结论的速度会快非常多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询