关于sklearn中mutual_info_score计算两列表互信息的疑问
关于
mutual_info_score的问题解析 核心误区
1. 函数输出范围的误解
mutual_info_score计算的是两个成对随机变量的互信息,公式为:
$$I(X;Y) = \sum_{x,y} p(x,y) \log\left(\frac{p(x,y)}{p(x)p(y)}\right)$$
它的单位默认是奈特(自然对数),也可通过base参数指定为比特。输出范围并非[0,1],最大值取决于变量的熵——比如你有8个手臂类别,单个变量的最大熵为$\log_2(8)=3$,所以得到1.94的结果是完全合理的,符合函数的计算逻辑。
2. 用法场景的错误
如果list_a和list_b是两个独立的访问记录序列(比如条件A下的所有访问、条件B下的所有访问),而非每个样本同时对应的两个特征,直接传入mutual_info_score是错误的。该函数会默认将两个列表按位置配对,当成联合样本计算,这和你要衡量两个分布相似性的需求不匹配。
符合需求的解决方案
你需要的是范围在[0,1]、满足“分布完全相同时为1,无重叠类别时为0”的指标,分两种场景处理:
场景1:成对样本(每个试验同时记录两个条件的访问手臂)
直接使用normalized_mutual_info_score,它会将互信息归一化到[0,1]区间:
from sklearn.metrics import normalized_mutual_info_score # 完全相同的情况 list_a = [1,2,1,3,4] list_b = [1,2,1,3,4] print(normalized_mutual_info_score(list_a, list_b)) # 输出1.0 # 无重叠的情况 list_a = [1,2,3,4] list_b = [5,6,7,8] print(normalized_mutual_info_score(list_a, list_b)) # 输出0.0
场景2:长度不同的独立序列(衡量两个分布的相似性)
用JS散度(Jensen-Shannon散度)转换,它专门用于衡量两个概率分布的相似度,转换后完全符合你的需求:
import numpy as np from scipy.spatial.distance import jensenshannon def distribution_similarity(list1, list2): # 合并所有类别,统一分布的类别空间 all_classes = np.unique(list1 + list2) # 统计每个类别的频率并转换为概率分布 count1 = np.array([list1.count(c) for c in all_classes]) count2 = np.array([list2.count(c) for c in all_classes]) p1 = count1 / count1.sum() p2 = count2 / count2.sum() # 计算JS散度并转换为相似性得分 js = jensenshannon(p1, p2, base=2) return 1 - js # 测试示例 list_a = [1,1,2,3] list_b = [1,1,2,3] print(distribution_similarity(list_a, list_b)) # 输出1.0 list_a = [1,2,3,4] list_b = [5,6,7,8] print(distribution_similarity(list_a, list_b)) # 输出0.0 list_a = [1,1,2] list_b = [1,2,2] print(distribution_similarity(list_a, list_b)) # 输出0.71(近似值,代表部分相似)
JS散度的范围是[0,1]:分布完全相同时为0,完全无重叠时为1,取1 - JS散度就得到你需要的“相似性得分”。
总结
- 不要用
mutual_info_score直接衡量独立序列的分布相似性,它的输出范围和场景都不符合你的需求。 - 成对样本用
normalized_mutual_info_score,长度不同的独立序列用基于JS散度的转换方法。
内容的提问来源于stack exchange,提问作者Kshtj
相关产品推荐
相关产品推荐

