ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MIC最大信息系数:用minepy实现非线性相关性分析

MIC最大信息系数:用minepy实现非线性相关性分析 简介这是一份围绕MIC最大信息系数算法实现与相关性分析的中文学习资源包面向数据挖掘、机器学习及需要处理非线性关系数据的开发者和研究者。内容以Python实现为主同时提供MATLAB、C/C等多语言版本源码并配有示例脚本、说明文档与测试文件便于对比不同实现方式并快速上手。压缩包共51个文件包括Python脚本、C/C源文件、RST/Markdown文档、图片及PDF等整体仅533KB轻量易下载。资源已吸引1536人学习。通过源码阅读与实际运行读者可以理解MIC算法的计算逻辑、参数设置及在金融、生物信息等场景下的应用学习如何借助minepy等工具进行非线性关联度量弥补传统相关系数难以捕捉复杂关系的不足。1. MIC相关性分析非线性关系检测为什么要换一个视角做相关性分析的人大概都遇到过这个尴尬皮尔逊算出来接近 0 的两个变量散点图却呈明显的U型或正弦波动。MIC最大信息系数正是为这类非线性关联检测而生的统计量配合 Python 里的 minepy 库几分钟就能把几十对变量的复杂依赖关系摸一遍。这套资源是一份完整的 minepy 源码包内含 C 核心实现、Python Cython 绑定、Matlab MEX 封装、示例脚本与测试文件等于同时拿到了算法本体和跨语言调用方式做特征工程、量化策略、生物信息分析都很用得着。下面我从原理、复现、踩坑到进阶验证完整过一遍确保你拿到手能直接跑出可信结果。2. MIC算法原理互信息、网格搜索与参数背后的取舍2.1 从互信息到最大信息系数MIC到底在算什么先建立直觉。皮尔逊相关本质上是在问“能不能用一条直线拟合这两个变量”而 MIC 问的是“把散点图切成多少块网格才能让两个变量的联合分布最明显地偏离独立分布”。后者不再假设任何函数形式所以正弦波、圆环、分段函数这类关系都能被它抓住。MIC 的数学基础是互信息。两个变量 X 和 Y 的互信息定义为I(X;Y) sum_{x,y} p(x,y) * log( p(x,y) / (p(x)p(y)) )它衡量的是“知道 X 之后Y 的不确定性减少了多少”。如果 X 和 Y 完全独立p(x,y)p(x)p(y)互信息为 0关系越强互信息越大。但互信息本身受量纲和数据量影响不能跨数据集比较所以 MIC 要做两件事归一化和最大化。归一化针对网格划分。把样本散点图按 x 方向切成 x 段、按 y 方向切成 y 段就得到一个 x 行 y 列的网格。每个格子里的点占比就是联合概率 p(x,y)按上式可以算出当前划分下的互信息再除以 log(min(x,y))得到 0 到 1 之间的归一化值。最大化则是在所有不超过上限的网格划分组合里取归一化互信息的最大值。这个“最大”非常关键真实存在的关系总能在某个网格分辨率下暴露出来而随机噪声在任何划分下都很难得到一致的高分。网格数量不能无限膨胀否则每个格子里就一两个点互信息一定虚高。minepy 用 B(n)n^alpha 限制总格子数alpha 默认 0.6。这块必须理解因为后面调计算速度、判断结果可信度全都要靠它。2.2 为什么用minepy与皮尔逊、斯皮尔曼的对比很多做数据分析的人第一反应是用 Excel 或者 pandas 自带的 corr()但那个默认算的是皮尔逊或斯皮尔曼。三种方法的能力边界差异很大直接决定了你该在什么场景选谁方法能捕捉的关联取值范围计算成本典型场景皮尔逊线性-1 到 1极快连续变量粗略筛查斯皮尔曼单调含部分非线性-1 到 1快排序数据、序数关系MIC任意形状非线性0 到 1较慢特征工程、复杂依赖发现斯皮尔曼相关性分析比皮尔逊强在能处理单调变化比如指数增长、对数衰减但它对正弦波这类非单调关系依然无能为力。MIC 不挑形状付出的代价是计算量。实战里我的习惯是先用皮尔逊和斯皮尔曼快速筛一遍把明显相关的变量挑掉再用 MIC 处理剩下的“未知关系”。还需要特别说明一点MIC 只给强度不给方向。0 代表完全独立1 代表完全可预测但它不会告诉你 X 增大时 Y 是变大还是变小。要判断方向得回到散点图或者配合皮尔逊的符号。这个特性很多人第一次用会忽略后面避坑章节再展开。回到这份资源本身。源码包里同时出现 mine.c/mine.hC 核心、minepy.pyxCython 绑定、libmine.pxdCython 的 C 接口声明、mine_mex.c 和 mine_mex.mexw64Matlab MEX 封装、mine.mMatlab 调用脚本说明原作者把算法核心做成了纯 C 库再向 Python 和 Matlab 分别暴露接口。这种结构的好处是Python 端只负责传参和收结果重活全在编译好的 C 代码里所以用起来比纯 Python 写的互信息计算快很多。如果你在的团队有人用 Python、有人用 Matlab这套资源一份就够不需要两边各写一套实现。2.3 三个参数决定结果质量alpha、c与estMINE 对象构造时只需要关注三个参数它们直接决定计算结果和运行速度。minepy 的 MINE 构造函数默认值是 alpha0.6c15estmic_approx。alpha 控制网格划分总数的上限 B(n)n^alphaalpha 越大网格越细理论上能捕捉更精细的关系但计算量也上去了。c 是单个维度上的最大划分数可以理解为“每个方向最多切多少段”超过这个值就不继续细分了。est 是估计器类型mic_approx 用近似算法跑得快mic_e 是精确版本更适合小数据量做严谨分析还有 mic_r2、mic_g 对应论文里的特定变体。实际调参时我一般这样定样本量在 500 到 5000 之间alpha0.6、c15 基本不用动样本超过 10 万alpha 降到 0.5 能省下大量时间MIC 值的变化通常不超过 0.02样本不足 100c 调到 10 以下避免网格划分过拟合噪声。est 默认 mic_approx 完全够用只有当你发现两组数据的 MIC 差一点就到阈值、需要更精确的排名时才换成 mic_e 重新算一遍。提示参数调整有一点“玄学”成分同一个数据集换了参数 MIC 值可能上下浮动 0.01 到 0.03。所以报告结果时一定要注明 alpha、c、est否则别人拿同一份数据复现出不同数值会怀疑你数据造假。3. minepy落地复现从安装、四类数据验证到批量计算MIC矩阵3.1 安装minepypip之外源码编译与许可证都要看拿到资源包之后第一件事是把 minepy 装进当前 Python 环境。安装前先确认 Python 和 pip 可用命令行敲 python -V 能看到 Python 3.8 说明环境没问题如果 pip 报错“不是内部或外部命令”多半是 Python 环境变量配置没做好先把 Python 的 Scripts 目录加进 PATH 再继续。# 检查 Python 和 pip 是否可用 python -V pip --version # 常规安装联网环境推荐 pip install minepy # 从源码包本地编译离线场景 / 二次开发 python setup.py build_ext --inplacepip 方式会自动拉取依赖并安装适合大多数用户。源码编译适合两种人一是公司内网环境装不了 pip 包二是你想改 minepy 的 Cython 层代码做二次开发。setup.py 编译完成后当前目录下会生成 minepy 的扩展模块Python 直接 import minepy 就能用。资源包里还有 compile_pyx.sh这是 Linux/macOS 下手动重新编译 Cython 模块的快捷脚本改完 .pyx 文件后跑一遍 ./compile_pyx.sh 即可。最后提醒一点商业使用场景资源包里带了 gpl-3.0.txtminepy 是 GPL-3.0 协议意味着你做闭源商业软件时直接调用会有许可证风险但做内部数据分析、学术研究完全没问题。这个坑很少有人在技术博客里提实际上很多公司风控会卡。3.2 四类数据验证线性、正弦、圆形与噪声装好之后先用四类已知模式的数据验证算法行为这比直接上真实数据靠谱得多。我造了 500 个样本分别测试线性、正弦、圆形、纯噪声四种情况同时算 MIC 和皮尔逊做对照。import numpy as np from minepy import MINE rng np.random.default_rng(42) n 500 x rng.uniform(-3, 3, n) # 四种模式线性、正弦非线性、圆形非单调、纯噪声 data { linear: (x, 2 * x 1 rng.normal(0, 0.3, n)), sine: (x, np.sin(x) rng.normal(0, 0.1, n)), circle: (np.cos(x * 2), np.sin(x * 2)), noise: (x, rng.normal(0, 1, n)), } for name, (a, b) in data.items(): m MINE(alpha0.6, c15) # 构造 MINE 对象 m.compute_score(a, b) # 训练数据计算网格与互信息 mic m.mic() # 最大信息系数 mas m.mas() # 最大关联强度 pearson np.corrcoef(a, b)[0, 1] print(f{name:8s} MIC{mic:.3f} MAS{mas:.3f} Pearson{pearson:.3f})这段代码的核心逻辑是先创建 MINE 对象再调用 compute_score 传入两组一维数组。compute_score 内部会完成网格划分、概率估计和互信息计算之后才能调用 mic()、mas() 等取值方法顺序不能反否则拿不到结果。MINE(alpha0.6, c15) 是常规配置样本量小于 100 时可以改成 MINE(alpha0.6, c10) 防止过拟合。运行结果里你会看到线性数据的 MIC 和皮尔逊都很高正弦数据的 MIC 接近 0.9 但皮尔逊接近 0圆形数据的 MIC 也能给到 0.3 以上但皮尔逊基本是 0纯噪声的 MIC 会掉到 0.1 左右。这正是 MIC 区分“有复杂关联”和“无关联”的直接证据。mas() 和 mic() 的区别在于mas 是原始网格下的最大关联强度mic 是经过等价性变换后的系数两者数值接近但含义略有差异报告时统一用 mic() 即可。3.3 批量构造MIC矩阵DataFrame两两计算的完整函数实际业务里很少只算两个变量大多数情况是手里一个 DataFrame 有几十列要找出哪些列之间存在强关联。这时需要一个批量函数把两两之间的 MIC 全部算出来输出一个对称矩阵。import pandas as pd import numpy as np from minepy import MINE def mic_matrix(df: pd.DataFrame, alpha0.6, c15): cols list(df.columns) mat pd.DataFrame(np.eye(len(cols)), indexcols, columnscols) for i in range(len(cols)): # 先转成 numpy 一维数组Cython 接口不接收 pandas Series a df[cols[i]].to_numpy(dtypefloat) for j in range(i 1, len(cols)): b df[cols[j]].to_numpy(dtypefloat) m MINE(alphaalpha, cc) m.compute_score(a, b) mat.loc[cols[i], cols[j]] m.mic() mat.loc[cols[j], cols[i]] m.mic() return mat mic_df mic_matrix(df, alpha0.6, c15) # 筛选 MIC 大于 0.3 的变量对按强度降序 pairs mic_df.where(mic_df 0.3).stack() print(pairs.sort_values(ascendingFalse).head(10))这里有几个容易出错的地方。to_numpy(dtypefloat) 是必须的minepy 的 compute_score 是 Cython 实现接口要求一维 double 数组直接传 pandas Series 会报类型错误。矩阵初始化用 np.eye 把对角线设为 1因为变量自己和自己的 MIC 必然是 1不需要额外计算。双重循环只算上三角再对称填到下三角省一半计算量。threshold 设 0.3 是个经验值MIC 低于 0.3 的关联在大多数业务场景里不值得深挖。这个函数的复杂度是 O(n^2)变量数超过 100 时计算量会明显增大建议先用方差或缺失率筛掉无用列再跑矩阵。另外 DataFrame 里如果有空值必须先 dropna 或 fillnaminepy 不接收 NaN。4. 避坑指南5个MIC实战翻车点第一个坑最隐蔽4.1 最大误区MIC的取值范围不是-1到1现象很多博客和教程在讲相关性分析时会把 MIC 写成“取值在 -1 到 1 之间0 表示没有关联1 表示正相关-1 表示负相关”。我最初照这个口径去解释实验结果被同事当场指出数值不可能为负。原因MIC 是归一化互信息的最大值互信息本身非负除以 log(min(x,y)) 之后依然落在 [0,1] 区间。它衡量的是“关联强度”不是“关联方向”。minepy 源码里 mic() 方法的返回值就是 [0,1] 的浮点数你永远不可能从它那里拿到负数。解决报告 MIC 时统一写成 [0,1] 区间并明确说这是强度指标。需要方向性结论时配合皮尔逊系数的符号或者直接看散点图。如果你的代码里算出了负的 MIC先检查是不是把两个变量搞反了或者数据预处理出了问题。4.2 四个实战问题安装、样本量、数据类型与跨语言差异问题一Windows 下 pip install minepy 报编译错误。现象执行 pip install minepy 后终端出现 fatal error C1083: Cannot open include file: Python.h安装过程直接中断。原因minepy 是老牌 Cython 包部分版本没有提供 Windows 预编译 wheelpip 在找不到安装包时会尝试本地编译而本机没有 Visual C 编译工具链和 Python 头文件。解决两种方案任选。一是安装 Microsoft C Build Tools勾选“使用 C 的桌面开发”后再重试 pip 安装二是直接用 conda 安装预编译版本conda install -c conda-forge minepy。我在 Windows 上用 conda 一次成功省去编译环境配置的麻烦。问题二样本量太小MIC 虚高到 1.0。现象只有 16 个样本的两列随机数算出来 MIC1.0怎么看都不合理。原因B(n)n^0.6n16 时 B5.28可尝试的网格划分组合非常少噪声数据也能在某个网格下碰出高分。样本量越小MIC 越容易过拟合。解决样本量至少 50 起步低于这个数不建议单独使用 MIC 下结论。如果数据确实少算完 MIC 后用第 5 章的排列检验判断显著性不要直接拿 1.0 当强关联的实锤。问题三compute_score 直接传 DataFrame 列报 TypeError。现象调用 m.compute_score(df[x], df[y]) 时报错 Argument x has incorrect type或者结果全是 nan。原因compute_score 是 Cython 函数要求两个参数都是 numpy 一维数组。pandas Series 虽然长得像数组但底层类型不满足接口要求NaN 值也无法参与概率估计。解决先做数据清洗再传参。df df.dropna()然后把列转成数组 df[x].to_numpy(dtypefloat)。这步看似多余但能省掉后面大量排查时间。问题四Python 算的 MIC 和 Matlab 算的结果对不上。现象同一份 CSV 数据用 minepy 算出 MIC0.61用资源包里的 mine.m 算出 0.59两边差了 0.02查代码都没错。原因Python 端 MINE 默认 estmic_approxMatlab 封装如果没显式传 est可能走了不同的估计器路径另外两端对网格上限的处理存在细微差别。解决做跨语言对比时两边统一显式指定参数Python 端用 MINE(alpha0.6, c15, estmic_e)Matlab 端调用 mine 时也传同样的 alpha、c、est。在正式项目里先拿一组已知关系的数据做基准验证确认两端输出一致后再大规模使用。资源包里 mine_mex.mexw64 是 Windows 64 位 MEX 文件如果你在 Linux 或 mac 上跑 Matlab需要先用 mex 命令重新编译 mine_mex.c。5. 让MIC结论立得住排列检验、特征筛选与热力图收口5.1 排列检验判断算出来的MIC是否显著假设你已经用前面的 mic_matrix 函数算出一个变量对 MIC0.61先别急着写进报告。0.61 到底可不可信要看它能不能超过随机打乱后的水平。排列检验是这里最直接的验证手段不需要额外装包rng np.random.default_rng(7) mic_obs 0.61 # 观测到的 MIC perm_mics [] for _ in range(1000): y_perm rng.permutation(y) # 打乱 y破坏与 x 的关联 m MINE(alpha0.6, c15) m.compute_score(x, y_perm) perm_mics.append(m.mic()) # p 值打乱后 MIC 超过观测值的比例加 1 防止出现 0 p (np.sum(np.array(perm_mics) mic_obs) 1) / 1001 print(fp {p:.4f})思路很直接把 y 的顺序打乱原本的关联结构被破坏此时 MIC 应该掉到噪声水平。重复 1000 次统计有多少次打乱后的 MIC 超过 0.61。如果超过的比例小于 0.05说明 0.61 不可能是巧合。这个方法的优势是完全不依赖假设的分布形态对非线性关系同样适用。5.2 热力图收口从数值矩阵到可读结论算出整个 MIC 矩阵之后光盯着数字很难发现模块结构我习惯用聚类热力图直接看块状分布import matplotlib.pyplot as plt import seaborn as sns sns.clustermap(mic_df, cmapYlOrRd, figsize(10, 8)) plt.show()clustermap 会自动对行列做层次聚类把 MIC 高的变量聚到同一个区块。看图的顺序是先找深红色大块那通常是强关联模块颜色越深MIC 越接近 1。如果列名太多导致横坐标标签挤成一团把 figsize 调大或者用 plt.xticks(rotation90) 旋转标签角度。这类“python 画图横坐标太密集”的问题处理方式在 MIC 矩阵这种宽数据场景里特别常见。从那以后我每次跑 MIC 都会强制先做一遍排列检验算完矩阵再出热力图最后才把结论写进分析报告。血泪经验告诉我MIC 的绝对值不能单独当结论尤其是小样本和高维数据场景。希望帮到你。本文还有配套的精品资源点击获取
返回列表