在数据分析和统计学中,我们经常需要面对一个核心问题:两个变量之间是否存在某种关系? 这种关系是强还是弱?是正向的还是负向的?为了回答这些问题,我们可以使用统计学中最强大且最常用的工具之一——皮尔逊相关系数。
在这篇文章中,我们将摒弃仅仅调用现成库函数的做法,而是深入探讨如何从零开始手动计算皮尔逊相关系数。通过这个过程,你不仅能够掌握计算公式背后的数学逻辑,还能更深刻地理解数据之间的线性关联。
什么是皮尔逊相关系数?
皮尔逊相关系数通常用字母 $r$ 表示。它用于衡量两个变量 $x$ 和 $y$ 之间的线性相关程度。为了准确使用它,我们需要了解以下几个关键特性:
- 取值范围:$r$ 的值总是介于 -1 和 1 之间。
* $r = 1$:完全正相关。这意味着当 $x$ 增加时,$y$ 也严格按比例增加。
* $r = -1$:完全负相关。这意味着当 $x$ 增加时,$y$ 严格按比例减少。
* $r = 0$:无线性相关。这表明变量之间没有线性关系。
- 绝对值大小:$r$ 的绝对值越接近 1,表示相关性越强;越接近 0,表示相关性越弱。
- 敏感性:皮尔逊系数主要捕捉线性关系。对于非线性关系(例如抛物线),它可能无法准确反映数据的真实联系。
核心公式与数学原理
为了计算这个系数,我们可以使用以下两种等价的公式。虽然现代计算通常使用协方差公式,但理解其展开形式能帮助我们明白每一步的计算来源。
方法一:基于协方差和标准差(最常用)
这是统计学中最简洁的表达方式,定义为协方差除以两个变量标准差的乘积:
$$ r = \frac{cov(x,y)}{\sigmax \sigmay} $$
展开后得到的计算公式如下:
$$ r = \frac{\sum(x-\bar{x})(y-\bar{y})}{\sqrt{\sum(x-\bar{x})^2}\sqrt{\sum(y-\bar{y})^2}} $$
其中:
- $cov(x,y)$:协方差,衡量两个变量如何共同变化。
- $\sigmax, \sigmay$:分别是 $x$ 和 $y$ 的标准差。
- $\bar{x}, \bar{y}$:分别是 $x$ 和 $y$ 的均值(平均值)。
- $n$:数据点的数量。
- $\sum$:求和符号,表示将所有数据点的计算结果加起来。
手动计算实战:分步指南
让我们通过一个经典的例子,一步步拆解计算过程。这种方法对于理解数据波动如何影响相关性至关重要。
示例 1:完美的线性正相关
假设我们有以下数据集,我们想探索变量 $x$ 和 $y$ 的关系。
数据集:
y
—
2
4
6#### 第一步:计算均值 ($\bar{x}$ 和 $\bar{y}$)
首先,我们需要找到数据的中心点。
- $\bar{x} = (1 + 2 + 3) / 3 = 2$
- $\bar{y} = (2 + 4 + 6) / 3 = 4$
#### 第二步:计算偏差和平方
接下来,我们为每个数据点计算以下值:
- $(x – \bar{x})$:$x$ 的偏差。
- $(y – \bar{y})$:$y$ 的偏差。
- $(x – \bar{x})^2$:$x$ 偏差的平方。
- $(y – \bar{y})^2$:$y$ 偏差的平方。
- $(x – \bar{x})(y – \bar{y})$:偏差的乘积(这是计算协方差的关键)。
计算表:
y
$y-\bar{y}$
$(y-\bar{y})^2$
—
—
—
2
-2
4
4
0
0
6
2
4
$\sum=8$
#### 第三步:代入公式
现在我们将求和结果代入皮尔逊公式:
$$ r = \frac{\sum(x-\bar{x})(y-\bar{y})}{\sqrt{\sum(x-\bar{x})^2}\sqrt{\sum(y-\bar{y})^2}} $$
$$ r = \frac{4}{\sqrt{2}\sqrt{8}} $$
$$ r = \frac{4}{\sqrt{16}} $$
$$ r = \frac{4}{4} = 1 $$
结果解读:
计算得出的皮尔逊系数为 1。这表明 $x$ 和 $y$ 之间存在完美的正线性依赖关系。在我们的数据中,实际上 $y = 2x$,这是一个完全确定的线性方程。
示例 2:处理小数与负相关
让我们看一个更复杂的数据集,数据不再呈现完美的倍数关系。
数据集:
y
—
9
1
2
8#### 计算过程
- 计算均值:
* $\bar{x} = 10 / 4 = 2.5$
* $\bar{y} = 20 / 4 = 5$
- 构建计算表(注意正负号):
y
$y-\bar{y}$
$(y-\bar{y})^2$
—
—
—
9
4
16
1
-4
16
2
-3
9
8
3
9
50
- 代入公式:
$$ r = \frac{-1.0}{\sqrt{5}\sqrt{50}} $$
$$ r = \frac{-1.0}{\sqrt{250}} \approx \frac{-1}{15.81} \approx -0.063 $$
(注:更精确计算 $\sqrt{250} = 5\sqrt{10} \approx 15.811$, $-1/15.811 \approx -0.063$)
修正计算:
让我们重新检查分母计算:
$\sqrt{5} \times \sqrt{50} = \sqrt{250} = \sqrt{25 \times 10} = 5\sqrt{10} \approx 15.81$
结果:$-1 / 15.81 \approx -0.06$。
等等,让我更正一下上面的计算表数据。如果我们仔细看数据,x增加时,y并没有明显的单调趋势。让我们重新精确计算一下分子:
$(-1.5 \times 4) + (-0.5 \times -4) + (0.5 \times -3) + (1.5 \times 3) = -6 + 2 – 1.5 + 4.5 = -1$。
结果确实是 -0.06 左右。
实际应用中的修正: 有时候数据看起来杂乱无章。接近 0 的值告诉我们,这两个变量之间几乎不存在线性关系。
示例 3:Python 代码实现(工程化视角)
虽然我们可以手动计算,但在实际的数据工程或软件开发中,我们通常使用 Python 的 numpy 库。我们可以手动实现该公式,以验证我们的数学逻辑。
让我们用代码来验证示例 1的情况。
import numpy as np
def calculate_pearson_manual(x_data, y_data):
"""
手动实现皮尔逊相关系数计算,不依赖内置的 corrcoef。
"""
n = len(x_data)
# 1. 计算均值
mean_x = sum(x_data) / n
mean_y = sum(y_data) / n
# 2. 初始化分子和分母
numerator = 0
sum_sq_diff_x = 0
sum_sq_diff_y = 0
# 3. 遍历数据进行累加
for i in range(n):
diff_x = x_data[i] - mean_x
diff_y = y_data[i] - mean_y
numerator += diff_x * diff_y
sum_sq_diff_x += diff_x ** 2
sum_sq_diff_y += diff_y ** 2
# 4. 计算分母
denominator = np.sqrt(sum_sq_diff_x) * np.sqrt(sum_sq_diff_y)
if denominator == 0:
return 0 # 防止除以零错误
return numerator / denominator
# 示例 1 的数据
x = [1, 2, 3]
y = [2, 4, 6]
r = calculate_pearson_manual(x, y)
print(f"手动计算的皮尔逊系数 r: {r}")
# 验证:使用 NumPy 内置函数
r_numpy = np.corrcoef(x, y)[0, 1]
print(f"NumPy 计算的皮尔逊系数 r: {r_numpy}")
代码解析:
这个函数完全模拟了我们刚才在纸上做的步骤。
- 安全性:我们在最后添加了一个
if denominator == 0的检查。这是一个很好的工程实践,因为如果一个变量是常数(所有数据都相同),标准差为 0,公式在数学上是无定义的。程序应当优雅地处理这种情况,而不是抛出异常。 - 可读性:我们将分子(协方差部分)和分母(标准差部分)的逻辑分离开来,这有助于调试。
常见错误与最佳实践
在计算相关性时,作为开发者我们需要警惕以下几个陷阱:
1. 混淆相关性与因果性
这是统计学中最经典的误区。即使我们计算出 $r = 0.99$,这并不意味着 $x$ 导致了 $y$。例如,冰淇淋销量和溺水事故的发生率可能高度正相关,但这并不意味着冰淇淋导致了溺水。它们背后有一个共同的原因——气温升高。我们在做数据分析时,一定要结合业务背景来解释结果。
2. 离群值的破坏力
皮尔逊系数对离群值非常敏感。仅仅一个错误的数据点就可能将 $r$ 从 0.8 拉低到 0.3,或者将其变成负数。
建议:在计算之前,务必绘制散点图进行可视化观察。如果发现明显的离群点,需要确认是数据记录错误还是真实的极端情况。
3. 非线性关系的误判
如果 $x$ 和 $y$ 之间存在很强的关系,但这种关系是曲线(例如 U 形),皮尔逊系数可能会接近 0。皮尔逊系数只衡量线性关系的强度。
解决方案:如果数据看起来呈现曲线关系,我们可以先尝试对数据进行转换(例如取对数 $\log(y)$),或者使用斯皮尔曼等级相关系数。
进阶示例:真实世界的数据波动
让我们看一个稍微不那么整齐的例子,看看我们如何解释结果。
数据集:
y
—
12
10
20计算步骤:
- 均值:
* $\bar{x} = 27/3 = 9$
* $\bar{y} = 42/3 = 14$
- 偏差表:
y
$y-\bar{y}$
$(y-\bar{y})^2$
—
—
—
12
-2
4
10
-4
16
20
6
36
56
- 最终计算:
$$ r = \frac{24}{\sqrt{18}\sqrt{56}} = \frac{24}{\sqrt{1008}} \approx \frac{24}{31.75} \approx 0.75 $$
结果解读:
$0.75$ 是一个相当强的正相关系数。这意味着虽然数据点不完全落在一条直线上(不像例1那样完美),但整体趋势非常明显:随着 $x$ 增加,$y$ 也有显著增加的趋势。
处理多变量数据与性能优化
当我们在处理大规模数据集(例如数百万行数据)时,使用 Python 原生循环会非常慢。我们应该使用 向量化 操作。
优化建议:
使用 NumPy 的广播机制,可以避免显式的 Python 循环,极大地提高性能。
import numpy as np
def optimized_pearson(x, y):
"""
利用 NumPy 向量化操作计算皮尔逊系数,适用于大规模数据。
"""
x = np.array(x)
y = np.array(y)
# 计算均值
mean_x = np.mean(x)
mean_y = np.mean(y)
# 向量化计算偏差
diff_x = x - mean_x
diff_y = y - mean_y
# 计算分子和分母
numerator = np.sum(diff_x * diff_y)
denominator = np.sqrt(np.sum(diff_x**2)) * np.sqrt(np.sum(diff_y**2))
return numerator / denominator
# 模拟大数据集
big_x = np.random.rand(1000000)
big_y = big_x * 2 + np.random.normal(0, 0.1, 1000000)
%timeit optimized_pearson(big_x, big_y) # 这种方式比循环快几百倍
总结
在这篇文章中,我们不仅学习了皮尔逊相关系数的公式,更重要的是,我们通过手动拆解计算过程,理解了“均值”、“偏差”、“协方差”和“标准差”是如何共同作用来衡量变量关系的。
关键要点回顾:
- 公式记忆:$r = \frac{Covariance(x,y)}{Std(x)Std(y)}$。分子是两者共变的部分,分母是为了消除量纲影响(标准化)。
- 取值含义:1 是完美正相关,-1 是完美负相关,0 是无线性相关。
- 实战技巧:先画散点图,警惕离群值,注意非线性关系。
无论你是正在准备数据科学面试,还是在开发推荐系统,掌握这个基础统计指标都是必不可少的技能。希望这篇文章能帮助你从原理到代码,彻底攻克皮尔逊相关系数!