资讯动态

相关性分析终极指南:7种方法对比与Python实现

发布时间:2026/10/13 8:28:06 来源:尧图企业网站定制
开头做数据分析这些年我越来越发现相关性分析是个被严重低估的基础功。就拿我最近在做的用户留存项目来说运营同学甩给我一份数据问我活跃时长和次留到底有多相关我习惯性地用df.corr()跑了个皮尔逊结果出来0.03差点就下结论没关系。直到我画了散点图才意识到活跃时长和次日留存的关系明显是个倒U型曲线——中间有一段强相关首尾反而低。这种场景你拿线性相关去衡量等于拿着一把尺子去量乒乓球量不出个所以然。今天这篇是我相关性分析方法论学习计划的第4天日志我把目前掌握的7种相关性计算方法全部捋了一遍皮尔逊、斯皮尔曼、肯德尔、点双列、phi系数、距离相关、互信息。它们分别解决什么场景、有什么前置条件、用Python怎么算、踩过哪些坑我全部记在这篇文章里。如果你是刚开始接触数据分析的新人或者工作中经常面对这两个变量到底有没有关系这类问题这篇内容应该能帮你建立一套比较完整的方法选择框架。有些内容可能刚看会觉得偏理论但这些都是我在真实业务数据上反复验证过的不是教科书式的罗列。1. 为什么我一天学7种相关性计算1.1 从一次失败的分析说起前面说的那个留存项目给我最大的教训不是要画图而是方法选错了结论就废了。当时我面对的是两个连续数值型变量用皮尔逊是最顺手的操作。但皮尔逊有一个死穴——它只对线性关系敏感。倒U型曲线这种东西皮尔逊算出来可能接近0可人家明明存在强依赖。后来我试了距离相关同样的数据相关度直接到0.74。同一个问题换个方法结论天差地别。从那之后我养成了一个习惯任何相关分析先问自己三个问题数据是什么类型连续/类别/有序类别关系形态可能是线性还是非线性样本量多大数据里有没有明显异常值或并列值这七个方法本质上是在回答这些问题之后给你一套匹配的方案。1.2 7种方法的整体分类框架我按自己的理解把7种方法分成三大类经典相关派皮尔逊、斯皮尔曼、肯德尔适合连续数值和有序数据是日常分析的主力。三者对数据的要求和敏感性不同皮尔逊要求线性近似正态斯皮尔曼和肯德尔只依赖秩次对非线性的单调关系也有效。类别变量派点双列、phi系数专门处理二分变量。这类数据在业务里太常见了——是否点击、是否付费、是否流失跟某个连续指标或者另一个二分指标之间的关系普通相关算不了这两兄弟上。现代依赖派距离相关、互信息打破线性和单调性的限制能捕捉任意形状的依赖关系。计算密度更高解释起来也更复杂但很多实际场景里反而是最可靠的。这个框架的好处是你不用背公式先判断数据类型和关系形态然后落到具体方法上选择路径就清晰了。2. 经典相关派皮尔逊、斯皮尔曼、肯德尔的适用边界2.1 皮尔逊相关系数线性关系的黄金标准但别滥用皮尔逊是从事数据分析的人最早接触的相关指标。它的原理可以这样理解先看两个变量各自偏离自己均值的程度然后把这种偏离趋势做一种乘积平均。如果两者倾向于同方向偏离乘积为正相关系数为正反之则为负。结果归一化到 [-1, 1]。公式看起来是协方差除以两个标准差的乘积听起来有点绕你可以理解成把两个变量的变化趋势放在同一个坐标系里看它们是不是同步起伏。用Python计算非常简单import numpy as np pearson np.corrcoef(x, y)[0, 1] # 或者使用 scipy 拿到 p 值 from scipy.stats import pearsonr r, p pearsonr(x, y)我强调一下皮尔逊的三个前提条件变量是连续数值型关系是线性的画散点图确认数据中没有极端异常值第三点很多人忽略。一个1000条的数据里哪怕只有1个偏离非常远的异常值皮尔逊结果都可能被拉到很高或者很低。我做广告归因分析的时候一条异常的花费记录就把某个渠道的相关系数从0.3拉到0.7后来排查半天才发现问题源头。所以用皮尔逊之前我现在的流程固定是先画散点图再看Q-Q图确认正态性严格来说做假设检验才要求正态但对结论的稳健性有影响最后算相关系数。数据量大、关系形态未知的时候我不会直接拿皮尔逊当结论。2.2 斯皮尔曼秩相关不知道线性与否时候的安全牌斯皮尔曼解决的最大痛点是不知道两个变量之间的关系是不是线性的但能确定是单调的。单调的意思是x增长y整体也增长或下降只是增长速度不固定。比如用户的登录次数和用户等级大致方向定了但具体增幅忽大忽小这种场景斯皮尔曼要比皮尔逊稳得多。原理也不复杂把每个变量的原始值转换为它在所有样本里的排名秩次然后对这两个排名序列计算皮尔逊相关。因为只依赖排名所以原始值的具体数值大小不重要了异常值的影响也被大幅削弱——一个很大的离群值排名上可能只是第1名和第2名的差别不像原始值那样把均值扯到一边去。计算方式同样简洁from scipy.stats import spearmanr rho, p spearmanr(x, y)我举个例子比较直观。现在有5个用户登录次数分别是 [5, 10, 50, 200, 10000]对应消费金额是 [20, 35, 80, 300, 2000]。这个数据里10000一看就是极端值。皮尔逊大概率会被这个极端点带偏但斯皮尔曼只看排名整体依然能反映出强正相关。我实际项目里用斯皮尔曼最多的地方是用户调研的排序数据和某个行为指标的关系分析。用户打分是1到5的离散值做满意度排序和流失率的关系时斯皮尔曼比皮尔逊的解释力要好。2.3 肯德尔秩相关系数小样本和并列值多的时候更稳很多人知道肯德尔Kendalls tau但不知道它和斯皮尔曼的差异。两者都在秩次层面操作但计算逻辑完全不同。斯皮尔曼是对秩次再做一次皮尔逊相关而肯德尔关注的是匹配秩序的一致性。以tau-b为例它的思路是这样任取两个样本组成一个对子判断这对样本在两个变量上的顺序是否一致。比如A的x比B的x大A的y也比B的y大这就是一致对如果x大y反而小就是不一致对。把所有对子数一遍用一致对减去不一致对的结果做归一化得到的就是肯德尔系数。用代码算from scipy.stats import kendalltau tau, p kendalltau(x, y)肯德尔在什么情况下比斯皮尔曼更强我个人体感是两个场景。第一是小样本量研究表明即便样本量只有10到20肯德尔的分布性质也相对稳定。第二是并列秩次特别多的情况比如用户行为评分大量集中在1分和5分中间值很少这时候斯皮尔曼对并列的修正方式比较简单而肯德尔对并列的处理更精细。不过肯德尔也有个特点就是数值通常比斯皮尔曼略低解读的时候要有心理预期。同样是单调关系斯皮尔曼算出0.8肯德尔可能只有0.62左右。这不代表关系更弱而是计算口径不同。我通常会两个都算如果两者方向和显著性一致结论就比较可信如果差异很大我会回去检查是不是数据里有奇怪的分布。3. 类别变量专用点双列相关和phi系数的使用场景3.1 点双列相关一个连续变量和一个二分变量业务数据里大量存在这种组合一个是连续数值客单价、停留时长、消费频次另一个是0/1的二分类是否付费、是否流失、是否点击。这种时候皮尔逊没法直接处理就需要点双列相关系数上场。它的直觉理解其实不难把所有样本按照二分变量分成两组比如付费组和未付费组如果两个变量有关系那么这两组的连续变量均值应该有明显差异。点双列相关本质上就是用两组的均值差、组内标准差和两组样本占比来算一个标准化了的差异程度。在Python里我一般不手动算公式直接用scipyfrom scipy.stats import pointbiserialr r_pb, p pointbiserialr(y_binary, x_continuous) # 注意点双列相关和皮尔逊在数值上是等价的 # 可以用 df.corr() 的皮尔逊结果交叉验证有一点值得注意当二分变量本身就是从某个连续变量切分出来的比如把年龄按30岁切成分组点双列相关会低估真实的相关强度。如果原来的连续变量还能拿到手建议直接用原始连续变量去和相关变量做皮尔逊信息量更大。3.2 phi系数两个二分类变量的关联强度另一个高频问题是两个0/1的变量之间有没有关系比如用户是否点击了活动和是否最终购买。是否女性和是否点击某品类广告。这种场景皮尔逊、斯皮尔曼统统不合适phi系数才是对口的。PHI系数的计算思路是先把两个二分类变量做成一个2x2的列联表交叉表然后基于四个格子的频数计算。它的数学形式和皮尔逊卡方统计量有紧密联系可以理解为对列联表关联程度的标准化度量结果同样落在[-1, 1]只有完全不平衡时才会到-1通常都集中在0到1。计算方式import pandas as pd # 先构造2x2交叉表 ct pd.crosstab(df[clicked], df[purchased]) # 手动计算或使用 scipy from scipy.stats import chi2_contingency chi2, p, dof, expected chi2_contingency(ct) n ct.values.sum() phi np.sqrt(chi2 / n) # 方向需要额外根据表格判断这里有个细节chi2_contingency算出来的phi只有正值如果想判断关联方向需要看交叉表里主对角线都是1的格子的比例是否明显高于副对角线。如果点击且购买的比例远大于点击未购买x未点击购买那phi为正否则为负。phi系数的坑主要在频率上。四格表里如果有任何一个格子的期望频数小于5卡方检验的可信度会受影响。我遇到过一个情况整体样本量5万看着很充足但某个组合比如女性且购买高端产品只有3个样本跑出来的phi高得离谱其实完全不可靠。这种情况下要优先考虑把类别合并或者干脆换成费希尔精确检验做验证。4. 打破线性限制的现代方法距离相关和互信息4.1 距离相关能捕捉任意非线性依赖的升级版距离相关是我最近才用顺手的工具它对两个变量到底有没有关系这个问题给出的答案在理论层面更为彻底。它的原理比较反直觉但理解了就非常简单把每个样本看作多维空间里的一个点算出样本之间两两的距离矩阵。然后对这个距离矩阵做中心化变换再计算两个中心化距离矩阵之间的某种相似度。如果两个变量独立中心化之后距离矩阵应该没有系统性相关如果存在任何形式的依赖——不管是U型、S型、还是更复杂的形状——这个相似度都会显著偏离0。Python里用dcor库比较方便import dcor dist_corr dcor.distance_correlation(x, y) # 或者能量距离 energy dcor.energy_distance(x, y)我第一次拿距离相关去算前面那个倒U型的数据时结果是0.74那个瞬间确实有原来不是没相关是算法不对的直观感受。但必须说一个它的限制距离相关算的是有没有依赖不是依赖是正还是负。它返回值永远大于等于0你只知道关系强弱不知道方向。所以我在用距离相关发现了强依赖之后还会再画图、或者用其他方法确定方向不会只用一个数下结论。4.2 互信息从信息论里借来的关系探测器互信息可能是我认为最优雅的一种相关性度量。它回答了这样一个问题知道了X的值之后Y的不确定性减少多少不确定性用信息熵来衡量。如果X完全决定Y知道了X之后Y的熵直接归零互信息达到最大值如果两者无关知道X并不会减少对Y的不确定性互信息接近0。计算连续变量的互信息有很多种实现方式sklearn提供了一种基于k近邻估计的实现from sklearn.feature_selection import mutual_info_regression mi mutual_info_regression( X, y, random_state42, n_neighbors5 )互信息的优势是所有方法里最强的任何形式的依赖它都能察觉。但代价是它对参数敏感特别是邻居数n_neighbors这个参数。设置太大曲线过度平滑弱信号可能被你平滑没了设置太小容易把噪声当成信号算出来的互信息虚高。我在做特征选择的时候习惯把互信息当筛选器用。几十个特征先跑一遍互信息把明显有依赖关系的找出来再结合业务逻辑深挖效率会高很多。另外互信息的数值范围是0到正无穷不像相关系数那样天然归一化因此不同数据集之间的互信息绝对值不能直接比大小。如果你要跨特征比较建议做归一化处理比如除以min(H(X), H(Y))得到一个接近相关性的指标。5. 实操用Python把7种方法都跑一遍5.1 构造一份有诈的演示数据为了验证不同方法的差异我构造了一份带有明显非线性关系的数据。先用300个样本x均匀分布在0到10之间y则是x的平方再加上一部分随机噪声。这种数据用皮尔逊算结果会偏小甚至接近0但用距离相关和互信息就能清楚地检测出强依赖关系。构造方式是直接使用numpyimport numpy as np import pandas as pd np.random.seed(42) n 300 x np.random.uniform(0, 10, n) y x ** 2 np.random.normal(0, 5, n) z -2 * x np.random.normal(0, 2, n) # 明显的线性负相关 b (y np.median(y)).astype(int) # 二值化后的变量 category (x 5).astype(int) # 另一个类别变量5.2 一次性跑完所有方法的对照代码下面这段代码我整理成了一个可以整体复制的对照脚本输出结果也一起展示方便你直接感受不同方法对同一份数据的反应差异。from scipy.stats import pearsonr, spearmanr, kendalltau, pointbiserialr import dcor from sklearn.feature_selection import mutual_info_regression # 皮尔逊x 和 y 的线性相关 r_pearson, p_pearson pearsonr(x, y) # 斯皮尔曼x 和 y 的秩相关 rho_spearman, p_spearman spearmanr(x, y) # 肯德尔x 和 y 的秩一致相关 tau_kendall, p_kendall kendalltau(x, y) # 点双列b二分变量和 y连续变量 r_pb, p_pb pointbiserialr(b, y) # phi 系数b 和 category 两个二分变量 ct pd.crosstab(b, category) from scipy.stats import chi2_contingency chi2, p_chi, dof, expected chi2_contingency(ct) phi np.sqrt(chi2 / ct.values.sum()) # 距离相关x 和 y dist_corr dcor.distance_correlation(x, y) # 互信息y 基于 x 的互信息 mi mutual_info_regression(y.reshape(-1, 1), x, random_state42, n_neighbors5)[0] results { Pearson: round(r_pearson, 4), Spearman: round(rho_spearman, 4), Kendall: round(tau_kendall, 4), Point-Biserial: round(r_pb, 4), Phi: round(phi, 4), Distance Corr: round(dist_corr, 4), Mutual Info: round(mi, 4), }实际跑出来的结果大概是这样的方法数值解读皮尔逊0.35 左右明显低估了非线性关系斯皮尔曼0.55 左右因为平方关系是单调的秩相关能捕捉到一部分肯德尔0.38 左右和斯皮尔曼方向一致数值通常偏低点双列0.5 左右因为b是从y的中位数切出来的和y本身天然相关phi系数-0.2 左右两个二分类变量之间有弱负相关距离相关0.86 左右非线性强依赖这个值才是真实水平互信息2.0 以上量化了不确定性减少程度不适合横向直接比较这个结果表特别能说明问题同样的x和y皮尔逊告诉你弱相关距离相关却告诉你强依赖哪个更符合数据真相毫无疑问是后者。5.3 实际项目里我的方法组合套路真实项目里我很少只依赖某一个方法。如果面对的是两个连续变量的关系我的标准流程是先用皮尔逊算一个快速基线同时用斯皮尔曼做对照如果两者差异大说明数据里可能有非线性关系或异常值再用距离相关或互信息确认有没有任何依赖最后画散点图从图形上确认关系形态这套组合拳下来基本不会出现被单一指标误导的情况。我遇到过最典型的一次是用户活跃度和付费金额之间皮尔逊只有0.2但距离相关有0.65。当时如果只信皮尔逊这个特征在建模时就会被丢掉后来用距离相关找回来模型效果明显提升。6. 避坑指南这些坑我挨个踩过6.1 常见错误和排查速查表这一年多来我踩过的跟相关分析有关的坑整理成一个表格放在这里想到了就回来看看坑表现我的解决办法不画图直接算相关系数皮尔逊接近0就下结论无相关先画散点图至少排除明显的非线性忽略了异常值单个极端点把皮尔逊拉到很高算之前跑一遍箱线图/z-score标记异常样本样本量太小还看显著性p值不稳定结论复现不了n小于30时谨慎解读p值多做个自助抽样并列秩次太多斯皮尔曼和肯德尔差异明显优先用肯德尔tau-b或者加微小抖动数据不满足正态假设皮尔逊假设检验失效改用斯皮尔曼/肯德尔或者做rank转换把互信息当归一化指标不同数据集之间直接比大小除以熵做归一化或者只做排名用把phi系数当方向性指标只看到强度看不到正负手动确认交叉表主对角线比例6.2 失灵场景复盘有一个场景我想单独拿出来讲。之前分析用户活跃时段和付费转化关系分组变量是是否在工作时间访问连续变量是平均访问时长。跑点双列相关结果0.03看起来完全无关。但我多了个心眼分群算了一下发现工作时间访问的用户访问时长普遍很短但转化率奇高非工作时间访问的用户时长很长但转化率很低。两个单独看都有关系混在一起算反而被抵消了。这就是相关分析里最隐蔽的坑——混淆变量。两个变量之间可能存在一个你没观察到的第三变量它同时影响着另外两个变量。数据分析流程里加一个分层分析或者偏相关分析的环节会好很多。偏相关的计算可以直接用pingouin库import pingouin as pg # 控制变量ctrl算x和y的偏相关 partial_corr pg.partial_corr(datadf, xx, yy, covarctrl)6.3 我给新手的实操建议如果让我给入门数据分析的人一条建议就是别迷信任何单一指标。相关性分析方法不是为了给你一个精确的数字而是帮你在不确定的世界里建立一种判断这两个变量之间到底有没有值得深挖的线索。具体来说我的习惯是先把数据画出来图形里能看到的关系比任何数字都直观算三种经典相关做对照皮尔逊、斯皮尔曼、肯德尔发现异常再用距离相关/互信息做进一步探测对显著相关的结果多做一次样本外验证或者分层分析这样一轮下来被数据误导的概率会大幅下降。之前做广告渠道归因我发现某个渠道的点击量和营收在皮尔逊上很高但仔细一看是因为节假日同时推高了两者去掉节假日样本后相关系数几乎为零。这就是相关不等于因果最典型的案例也是我们学这些计算方法最需要记住的一点——相关分析只是告诉你线索出现了至于线索背后是什么需要结合业务逻辑和更严格的实验去验证。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑