皮尔森相关系数从原理到工程实践:相似度计算的基石方法详解
发布时间:2026/10/13 20:13:18 锦皓数字建站

做数据分析或者算法开发的应该都碰过这样一个问题两个东西到底像不像。推荐系统里要判断用户口味是否一致风控里要看两个特征有没有同涨同跌特征工程时要排查哪些变量互相冗余。这时候第一个冒出来的方法往往就是皮尔森相关系数。皮尔森相关系数用一句话概括就是衡量两个连续变量之间线性相关程度的数值范围在-1到1之间。负数代表负相关正数代表正相关接近0则说明没有明显的线性关系。这篇先把皮尔森相关系数从公式到代码、从原理到坑完整过一遍。刚接触数据分析和统计的同学可以拿它当入门教程已经在业务里写推荐算法、做特征筛选的开发者也能从中看到一些平时容易忽略的细节比如缺失值处理、常量列导致的分母为零、样本量太小导致结果虚高。这些都是我实际项目里踩过的坑整理出来供你参考。1. 相似度计算场景与皮尔森系数的定位1.1 哪些真实场景需要计算相似度相似度计算不是一个孤立的技术点它藏在很多常见任务的背后。做协同过滤推荐的时候要计算用户和用户之间的相似度找出口味相近的人也要计算物品之间的相似度找出看过A的人可能也喜欢B。做聚类分析的时候样本之间的相似度直接决定了它们会被分到同一个簇里像层次聚类、DBSCAN这类算法本质上都是在相似度或距离的基础上展开的。做特征工程的时候如果两个特征皮尔森相关系数达到0.95以上业务上基本可以认定它们携带了重复信息留一个就够否则会影响模型稳定性和可解释性。还有一些更细的业务场景。电商平台要做新人猜你喜欢冷启动阶段没有用户行为数据只能拿商品属性向量算相似度把相似商品推荐给同类人群。金融风控里做多头借贷关联挖掘也要看不同指标之间是不是高度相关判断是否存在共线性的风险。生物信息里分析基因共表达网络同样是基于基因表达谱的相似性。可以说只要是从数据里找关系相似度计算就是绕不开的底层工具。在这些场景里皮尔森相关系数又是最常用的一个起点。不是因为它一定最好而是因为它在大多数连续变量场景下表现稳定、计算简单、解释直观而且很多工具库都内置了现成实现随便一行代码就能跑出结果。1.2 为什么皮尔森通常是我选相似度指标时的第一选择我自己的习惯是如果数据是连续型数值且没有明显理由说要改用其他指标那默认就用皮尔森相关系数。原因其实在于一个很关键的细节——它做了中心化处理。举一个推荐系统里的经典例子。用户A给五部电影分别打了4、3、5、2、4分用户B给了3、2、4、1、3分。仔细看一下B其实每部电影都比A少打1分说明两个人的喜好顺序完全一致A喜欢的B也喜欢A不喜欢的B也不喜欢。这种情况下两个人应该有非常高的相似度。用皮尔森相关系数算结果是1完美正相关。但如果用余弦相似度算结果会低不少因为余弦相似度是从原点出发衡量向量夹角它没法区分两个人打分普遍偏高和两个人打分普遍偏低这类水平差异。皮尔森先减去各自的均值把两个评分向量的中心都挪到原点剩下的波动模式一对比自然就识别出了完全一致的趋势。这个特性在真实评分数据里特别重要因为用户打分习惯差异非常大。有人手松普遍给4分以上有人苛刻很少超过3分。如果不用皮尔森这种中心化的方法相似度会被打分习惯干扰而不是真实偏好。所以做协同过滤的时候评分数据的用户相似度很多团队都优先用皮尔森道理就在这里。1.3 皮尔森与几种常见相似度方法的对照不同相似度计算方法的侧重完全不同选错方法会直接影响结果质量。我做了一个简单的对照表方便你按场景快速定位。方法核心关注点典型范围适用场景皮尔森相关系数数值波动趋势是否一致做了中心化-1 到 1评分数据、连续变量、线性相关分析余弦相似度向量方向是否一致不中心化-1 到 1文本向量、图片向量、高维稀疏向量欧氏距离绝对数值差距0 到 无穷数值大小本身有意义的场景斯皮尔曼秩相关系数单调关系基于排名-1 到 1非线性但单调递增或递减的关系简单解释一下余弦相似度适合文本向量因为词频的绝对大小意义不大方向才是重点欧氏距离适合坐标、物理量这类数值本身很重要的数据皮尔森可以看成是中心化后的余弦相似度所以它多了一步消除水平差异的操作。如果数据呈现明显的非线性单调关系比如排名和收入的关系那斯皮尔曼更合适。2. 皮尔森相关系数的工作原理拆解2.1 从协方差到相关系数皮尔森相关系数的公式长这样r cov(X, Y) / (s_x * s_y)其中cov(X, Y)是X和Y的协方差s_x是X的标准差s_y是Y的标准差。展开来看协方差的定义是cov(X, Y) Σ(x_i - x̄)(y_i - ȳ) / (n - 1)这里的x̄是X的均值ȳ是Y的均值n是样本量。分子部分把每个样本点的x离差和y离差相乘再求和这个乘积的符号非常直观如果x大于均值时y也大于均值乘积为正如果x大于均值时y反而小于均值乘积为负。所以把所有乘积加总之后正的多说明两个变量倾向于同方向变化负的多说明倾向于反方向变化。协方差的大小和变量本身的量纲有直接关系比如用元和用万元表示同一批收入数据协方差的数值会差一万倍。这就没法直接拿来做比较了。所以皮尔森在协方差的基础上再除以两个标准差标准差也是带量纲的一除就把量纲抵消掉了变成一个纯粹的、没有单位的数值取值范围也锁定在-1到1之间。这里有个小细节可能有人会纠结分母为什么用n-1而不是n。其实对相关系数来说这个选择不影响最终结果。因为分子分母都含有一个系数约分之后就抵消了。你在用软件计算时无论底层用的样本方差还是总体方差得到的皮尔森相关系数是一样的。2.2 中心化到底在干什么中心化就是让每个变量减去自己的均值。这一步看起来简单但它本质上是把坐标原点移动到了数据的重心位置。还是拿两个用户的评分向量举例。A和B的原始向量在五维空间里是两个点如果直接算余弦原点在坐标零点夹角同时受到打分位置和波动方向的影响。减掉均值之后向量变成了离差向量比如A变成了[0.4, -0.6, 1.4, -1.6, 0.4]B变成了[0.4, -0.6, 1.4, -1.6, 0.4]这时候原点在均值位置向量只保留波动信息。再算余弦得到的就是纯粹的线性趋势一致性。可以这样记皮尔森相关系数等价于对两个变量分别做标准化后的余弦相似度。对数据做标准化减去均值、除以标准差再算余弦相似度结果和皮尔森完全一样。这个联系非常有用能帮你理解为什么皮尔森对水平差异免疫对趋势差异敏感。我在实际项目里就遇到过这种情况两个地区门店的销售数据一个店月均销售额是另一个店的三倍但月度涨跌波动几乎一模一样。用欧氏距离看两个店差得很远用皮尔森看相关系数0.96说明它们受同样的季节因素驱动。这两种结论都有价值取决于你想分析规模差异还是波动规律。2.3 相关系数的取值该如何解读皮尔森相关系数的取值落在-1到1之间解读的时候可以这样划分r 1完全正线性相关所有点都落在一条斜率为正的直线上。r -1完全负线性相关所有点都落在一条斜率为负的直线上。r 0没有线性相关关系两点之间不存在明显的线性趋势。0 r 1正相关x增大时y倾向于增大。-1 r 0负相关x增大时y倾向于减小。经验上看|r|在0.8以上可以认为是强相关0.5到0.8算是中等偏强0.3到0.5算较弱0.3以下基本可以忽略。不过这个标准不是绝对的不同行业差异很大。金融领域两个收益率因子相关性超过0.6就高度警惕了而心理学问卷里题目和总分之间0.4的相关性已经算不错。所以解读时要结合领域经验别死套经验值。还有一个重要的点r的平方r²可以理解成一个变量能被另一个变量线性解释的变异比例。r0.8时r²0.64意思是Y的变异中有64%可以用X的线性变化来解释。这个说法在业务汇报里很常用比直接说r等于0.8更有直观意义。3. 从零开始算一个完整实例3.1 构造场景与原始数据为了把原理落到实操上我用一个模拟的电影评分场景来演示。假设有三个用户对五部电影打分评分范围是1到5分。用户A对电影M1到M5的评分是A [4, 3, 5, 2, 4]用户B的评分是B [3, 2, 4, 1, 3]用户C的评分是C [1, 5, 2, 4, 3]先从业务角度猜一下A和B每部电影都差1分趋势大概率完全一致。C看起来和A、B在很多电影上是反着来的比如A很喜欢第三部电影打了5分C只给2分A不太喜欢第四部电影给2分C反而给了4分。下面用皮尔森把这种直觉量化出来。3.2 手工逐步计算先算A和B的相关系数。第一步算均值A的均值 (4 3 5 2 4) / 5 3.6B的均值 (3 2 4 1 3) / 5 2.6第二步算离差也就是每个值减去均值A的离差 [0.4, -0.6, 1.4, -1.6, 0.4]B的离差 [0.4, -0.6, 1.4, -1.6, 0.4]这里已经能看出来A和B的离差完全一样所以相关系数铁定是1。继续往下算是为了验证流程。第三步算协方差。把对应位置的离差相乘0.4 * 0.4 0.16 -0.6 * -0.6 0.36 1.4 * 1.4 1.96 -1.6 * -1.6 2.56 0.4 * 0.4 0.16求和得到5.2除以n-1也就是4协方差 1.3。第四步算各自的标准差。A的离差平方和是5.2方差是5.2/41.3标准差是根号下1.3约等于1.140。B同理标准差也是1.140。第五步代入公式r 1.3 / (1.140 * 1.140) 1.3 / 1.2996 ≈ 1结果就是1A和B的评分趋势完全一致。再算A和C的相关系数。C的均值是C的均值 (1 5 2 4 3) / 5 3.0C的离差 [-2, 2, -1, 1, 0]协方差分子逐项相乘0.4 * (-2) -0.8 -0.6 * 2 -1.2 1.4 * (-1) -1.4 -1.6 * 1 -1.6 0.4 * 0 0求和为-5.0除以4得到协方差 -1.25。C的标准差离差平方和是4 4 1 1 0 10方差是10/42.5标准差约等于1.581。代入公式r -1.25 / (1.140 * 1.581) ≈ -1.25 / 1.803 ≈ -0.693所以A和C的皮尔森相关系数约为-0.693中等偏强的负相关。这个结果符合直觉C在很多电影上的喜好确实和A相反但并不是所有电影都完全相反。3.3 用代码验证并输出相似度矩阵手工算完一遍之后再用代码验证能确保结果一致。我用的是Python。import numpy as np A np.array([4, 3, 5, 2, 4]) B np.array([3, 2, 4, 1, 3]) C np.array([1, 5, 2, 4, 3]) # 方式一np.corrcoef直接算两两相关 print(np.corrcoef(A, B)) # 输出 [[1, 1], [1, 1]] print(np.corrcoef(A, C)) # 输出 [[1. , -0.69337525], [-0.69337525, 1.]]如果想把三个用户全部两两算一遍可以直接把多个向量合成二维矩阵传给np.corrcoefdata np.array([A, B, C]) corr np.corrcoef(data) print(corr)输出结果:[[ 1. 1. -0.69337525] [ 1. 1. -0.69337525] [-0.69337525 -0.69337525 1. ]]矩阵对角线是1因为每个向量和自身的相关系数天然是1。A和B是1A和C约-0.693B和C也是约-0.693。这和手工计算结果完全吻合。如果还想顺便看显著性p值用SciPy的pearsonr更方便from scipy.stats import pearsonr r, p pearsonr(A, C) print(r, p)在业务代码里我通常会封装一个更健壮的相似度计算函数把缺失值和常量列的情况也处理掉后面遇到问题不会直接崩def pearson_sim(x, y): x np.asarray(x, dtypefloat) y np.asarray(y, dtypefloat) # 过滤掉任意一侧缺失的位置 mask ~(np.isnan(x) | np.isnan(y)) x, y x[mask], y[mask] # 样本太少或某个变量是常量时相似度定义为0 if len(x) 2 or np.std(x) 0 or np.std(y) 0: return 0.0 return np.corrcoef(x, y)[0, 1]再配合一个生成相似度矩阵的函数做协同过滤时直接用def similarity_matrix(data): n data.shape[0] sim np.eye(n) for i in range(n): for j in range(i 1, n): s pearson_sim(data[i], data[j]) sim[i, j] sim[j, i] s return sim4. 常见问题与排查技巧实录4.1 数据里有缺失值相关系数还能算吗真实数据里缺失值是无法避免的。用户可能没看过某部电影传感器可能在某个时段没采集到数值。如果直接拿带NaN的数组去算np.corrcoef结果会直接返回nanscipy的pearsonr也会报错。处理方式要看情况。如果数据量很大、缺失比例低最简单的方法是按有共同数据的样本来计算也就是pairwise complete只用两边都非空的点。pandas的DataFrame.corr()默认就是这个逻辑所以用pandas算相关系数时通常不报错。如果自己做封装就要像我上面写的pearson_sim那样先把缺失位置过滤掉再算。如果某个特征缺失率特别高比如超过一半的人都缺那这个特征本身就很难支撑可靠的相关系数与其硬算不如考虑删掉或用领域知识填充。还有一种情况是时间序列的前后缺失可以用前向填充、后向填充或者线性插值补上但要注意插值本身会引入人为的相关性需要谨慎判断。4.2 遇到常量序列导致分母为零皮尔森相关系数的分母是两个标准差。如果某个变量在所有样本点上的取值完全一样那它的标准差就是0分母为0结果就会变成NaN或者出现除零问题。这种情况在业务数据里并不少见。比如一个电影刚上映没多久所有拿到它评分的人都只打了一个固定分数或者某个商品在某个时期的销量恒定不变这些列本质上没有携带任何信息计算相似度毫无意义。处理方案很大程度取决于你的目的。如果是做特征筛选常量列直接删掉因为它们对模型没有任何区分度。如果是计算用户相似度某一个用户给所有电影都打了同一个分比如全是4分那他和任何人的相似度都不可靠建议在业务规则上单独处理比如纳入信息量不足的候选名单或者直接赋一个默认值。在代码层面就是先判断std是不是0是的话就返回0或None避免让异常结果往下游传递。4.3 皮尔森检测不了非线性关系皮尔森只能检测线性关系这是它最大的局限。如果两个变量之间存在明显但非线性的关系皮尔森相关系数可能接近0但这不代表两个变量没有关联。最经典的例子就是抛物线关系。假设x在-3到3之间取整数y x²。x和y之间明明存在严格的函数关系但皮尔森相关系数会非常接近0因为数据在均值两侧上下对称分布线性趋势互相抵消。如果你只看相关系数会误以为x和y毫不相关这就踩了大坑。所以我在实际分析里有个习惯算相关系数之前一定先画散点图。一眼扫过去数据是条直线、是条曲线、还是均匀的一团云心里就有数了。如果散点图呈现单调但弯曲的形态我就改用斯皮尔曼秩相关系数它基于排名计算能捕捉单调关系对非线性也有效。如果散点图呈现出明显的周期或更复杂的结构那就要考虑分区间建模或者其他方法了。4.4 样本量太小数字再高也别急着下结论皮尔森相关系数的可靠性严重依赖样本量。样本量很小的时候几个巧合的数据点就能造出极高的相关系数但对总体的推断价值非常有限。举个简单的例子只用三个样本点很容易找出一条几乎完美拟合的直线r可能高达0.99但这三个点完全可能是偶然。统计上要判断相关性是否显著一般用t检验统计量是t r * sqrt(n - 2) / sqrt(1 - r²)自由度是n-2。从这个公式能看出来样本量n越小同样的r对应的t值越小p值越大。n3的时候只有1个自由度就算r接近1也不能自信地下结论说两个变量在总体层面相关。我在给业务方出分析结论时不会只报一个r值而是连同样本量和p值一起给。一般建议样本量至少30以上最好能有100以上这样相关系数才相对稳定。如果只能拿到小样本那就明确标注结果仅供参考需要后续验证。4.5 离群值会把结果带偏离群值对皮尔森相关系数的影响非常大。因为公式里用的是均值、标准差和乘积和一个离群点就能让均值发生偏移、让方差变大、让乘积和失衡从而大幅改变相关系数。我做过一个模拟原本一组变量相关系数只有0.2往数据里塞一个极端离群点之后r直接飙到0.85。反过来也存在一个离群点让原本0.9的相关性变成0.3。这不是夸张而是实际会发生的事情。排查方法先说老办法画散点图最直观离群点会明显掉在主体数据区域之外。也可以用标准化之后的Z分数把超过3个标准差的点标出来检查。处理策略上先判断离群点是不是脏数据比如录入错误、量纲搞错是的话就直接修正或剔除。如果离群点代表真实的特殊情况那就不能简单删除可以考虑用斯皮尔曼秩相关这种对离群值不敏感的方法或者做稳健的相关系数估计。经过几次教训之后我现在看任何相关系数结果都会同时看散点图和样本量单独一个数字很少能真正说明问题。最后写点实在话我个人在实际操作中最大的体会是皮尔森相关系数从来不是算出来就完事它更像是数据分析流程的起点而不是终点。拿到一个r值先搞清楚它背后是不是存在线性假设再确认样本量够不够有没有异常值干扰最后还要结合业务场景判断这个相关有没有实际意义。有时候r不高但业务逻辑上两个指标强相关有时候r很高却只是数据噪声和巧合造成的假象。一个小技巧分享给你当你算出一批相关系数之后把皮尔森和斯皮尔曼的结果放在一起对比看。如果两个结果差异很大往往意味着数据里存在离群值或者非线性模式这时候别急着用某一个结论先回头检查数据再下判断。这个方法帮我校正了好几次分析方向省了不少返工成本。皮尔森相关系数后续还可以往多个方向扩展比如做加权皮尔森、分层的相关分析、偏相关分析等等。每一类都对应不同的业务问题等下次碰到再说。这一篇内容已经足够覆盖日常80%的使用场景你可以直接拿去复现和验证。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。