掌握相关性分析:读懂数据间的“悄悄话”
<p>在数据分析的江湖里,我们经常会听到老板或业务方抛出这样的问题:</p><ul>
<li>“现在的年轻人越晚睡,买护肤品是不是越疯狂?”</li>
<li>“我们APP的各种优惠券,真的能提升用户的留存率吗?”</li>
<li>“天气越热,这只股票是不是跌得越惨?”</li>
</ul>
<p>面对这些问题,很多新人容易犯 <strong>“凭感觉”</strong> 的错误:“我觉得应该有关系吧……”</p>
<p><strong>数据分析不相信“我觉得”,只相信证据。</strong> 而寻找变量之间关系强弱的这个过程,就叫做<strong>相关分析</strong>。</p>
<p>今天,就带大家把相关分析的工具箱翻个底朝天,从基础到进阶,一次性讲透!</p>
<h1 id="1-什么是相关分析">1. 什么是相关分析?</h1>
<p>简单来说,相关分析就是判断<strong>两个或多个事物之间是否存在某种联系,以及这种联系有多紧密</strong>。</p>
<p>但请务必记住数据分析界的第一铁律:<strong>相关 <strong>$ \neq $</strong> 因果</strong>。</p>
<ul>
<li><strong>相关</strong>:公鸡叫了,天亮了。(它俩有关系,经常一起发生)</li>
<li><strong>因果</strong>:因为公鸡叫了,所以天亮了。(这就错了,天亮是因为地球自转,不是因为鸡叫)</li>
</ul>
<p>我们要做的,就是用数值(相关系数)来量化这种“一起发生”的程度。</p>
<h1 id="2-数据相关的三剑客">2. 数据相关的“三剑客”</h1>
<p><strong>皮尔森相关系数</strong>,<strong>斯皮尔曼相关系数</strong>和<strong>肯达尔相关系数</strong>,这是最常见的三种相关系数,它们处理的是<strong>数值型</strong>或者<strong>有等级顺序</strong>的数据。</p>
<h2 id="21-皮尔森相关系数精确测量的标准">2.1. 皮尔森相关系数:精确测量的标准</h2>
<p><strong>皮尔森相关</strong>是最常用的相关性分析方法,适用于符合以下条件的数据:</p>
<ul>
<li>连续数据(定距或定比尺度)</li>
<li>数据服从正态分布</li>
<li>变量间呈线性关系</li>
</ul>
<p>这是最“挑剔”也是最常用的指标。它要求数据是<strong>连续数值</strong>(定距/定比),并且最好服从<strong>正态分布</strong>(钟形曲线)。它衡量的是<strong>线性关系</strong>(是不是一条直线)。</p>
<p>比如:<strong>身高和体重</strong>。一般来说,人越高,体重越重,这是一个比较标准的线性关系。</p>
<p>它的取值范围从 <code>-1</code> 到 <code>1</code>。接近 1 表示<strong>正相关</strong>(同涨同跌),接近 -1 表示<strong>负相关</strong>(此消彼长),0 表示<strong>没关系</strong>。</p>
<p>代码示例如下:</p>
<pre><code class="language-python">import numpy as np
import pandas as pd
from scipy import stats
# 模拟数据:运动时间(小时/周)与体重指数(BMI)
np.random.seed(42)
# 生成100个样本
n_samples = 100
exercise_time = np.random.normal(5, 2, n_samples)# 平均每周运动5小时
# BMI与运动时间负相关(运动越多,BMI越低)
bmi = 25 - 0.5 * exercise_time + np.random.normal(0, 1.5, n_samples)
# 创建DataFrame
data = pd.DataFrame({'运动时间_小时每周': exercise_time, 'BMI': bmi})
# 计算皮尔森相关系数
pearson_corr, p_value = stats.pearsonr(data['运动时间_小时每周'], data['BMI'])
print(f"皮尔森相关系数: {pearson_corr:.3f}")
print(f"P值: {p_value:.5f}")
# 运行结果:
'''
皮尔森相关系数: -0.614
P值: 0.00000
'''
</code></pre>
<p>图形展示的效果如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/83005/202512/83005-20251219160453552-1117963478.png" alt="" loading="lazy"></p>
<h2 id="22-斯皮尔曼相关系数打破正态分布的限制">2.2. 斯皮尔曼相关系数:打破正态分布的限制</h2>
<p>如果数据<strong>不服从正态分布</strong>,或者有<strong>极端值</strong>(比如马云的财富混进了我们的收入数据中),<strong>皮尔森相关系数</strong>就不准了。</p>
<p>这时候用<strong>斯皮尔曼相关系数</strong>。它看重的是排名(Rank),而不是具体数值。</p>
<p>比如语文成绩排名和数学成绩排名,我们不关心具体考了多少分,只关心你的位次。</p>
<p>代码示例如下:</p>
<pre><code class="language-python"># 模拟数据:社交媒体表现
np.random.seed(42)
# 生成非正态分布的数据
followers = np.random.exponential(5000, 50)# 指数分布
likes = 0.1 * followers**1.2 + np.random.normal(0, 1000, 50)# 非线性关系
# 创建DataFrame
social_data = pd.DataFrame({"粉丝数": followers, "平均点赞数": likes})
# 计算斯皮尔曼相关系数
spearman_corr, spearman_p = stats.spearmanr(
social_data["粉丝数"], social_data["平均点赞数"]
)
print(f"斯皮尔曼相关系数: {spearman_corr:.3f}")
print(f"P值: {spearman_p:.5f}")
# 运行结果:
'''
斯皮尔曼相关系数: 0.857
P值: 0.00000
'''
</code></pre>
<p>图形化结果如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/83005/202512/83005-20251219160453570-1732482995.png" alt="" loading="lazy"></p>
<h2 id="23-肯达尔相关系数小样本和有序数据的首选">2.3. 肯达尔相关系数:小样本和有序数据的首选</h2>
<p><strong>肯达尔相关</strong>也适用于等级数据,与<strong>斯皮尔曼相关</strong>不同,它更关注“和谐对”与“不和谐对”。</p>
<p>通常用于样本量较小,或者数据有很多并列排名的情况。</p>
<p>比如:<strong>两位面试官给5个候选人打分</strong>。我们要看这两位面试官的审美标准是否一致。</p>
<p>代码示例:</p>
<pre><code class="language-python"># 模拟数据:电影评分与票房
np.random.seed(42)
# 生成有序数据(电影评分和票房排名)
movie_data = pd.DataFrame(
{
"电影名称": ,
"评分等级": np.random.choice(
, 20, p=
),# 1-5星
"票房排名": np.arange(1, 21),# 票房排名
}
)
# 添加一些相关性:评分越高,票房排名越好(数字越小)
for i in range(len(movie_data)):
if movie_data.loc >= 4:
movie_data.loc = max(
1, movie_data.loc - np.random.randint(3, 8)
)
elif movie_data.loc <= 2:
movie_data.loc = min(
20, movie_data.loc + np.random.randint(3, 8)
)
# 计算肯德尔相关系数
kendall_corr, kendall_p = stats.kendalltau(
movie_data["评分等级"], movie_data["票房排名"]
)
print(f"肯德尔相关系数: {kendall_corr:.3f}")
print(f"P值: {kendall_p:.5f}")
# 运行结果:
'''
肯德尔相关系数: -0.503
P值: 0.00460
'''
</code></pre>
<p>图形化结果如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/83005/202512/83005-20251219160453489-583942045.png" alt="" loading="lazy"></p>
<h1 id="3-偏相关分析谁是第三者">3. 偏相关分析:谁是“第三者”?</h1>
<p>有时候,两个变量看起来关系很好,其实是因为有“第三者”在捣乱。</p>
<p><strong>偏相关分析</strong>允许我们在控制其他变量的情况下,分析两个变量之间的 <strong>"纯"</strong> 相关性。</p>
<p>比如一个生活中的案例:<strong>“冰淇淋销量”</strong> 和 <strong>“溺水事故数量”</strong>。<br>
数据统计发现,冰淇淋卖得越好的日子,溺水的人越多(相关系数很高)。难道吃冰淇淋会导致溺水?<br>
当然不是!背后的控制变量(第三者)是<strong>气温</strong>。气温高 -> 买冰淇淋多 & 游泳的人多 -> 溺水概率大。</p>
<p>想要正确分析,必须<strong>剔除</strong>控制变量(气温)的影响后,再看另外两个变量(冰淇淋和溺水)是否还相关。</p>
<p>下面的示例中,我们先排除收入影响后,分析教育水平与消费水平的关系。</p>
<pre><code class="language-python"># 使用pingouin库进行偏相关分析(需要安装:pip install pingouin)
import pingouin as pg
# 模拟数据:教育水平、收入和消费水平
np.random.seed(42)
n = 100
# 教育水平(1-5,5为最高)
education = np.random.choice(, n, p=)
# 收入与教育水平正相关
income = 30000 + 15000 * education + np.random.normal(0, 5000, n)
# 消费水平与收入和教育水平都相关
consumption = 1000 + 0.3 * income + 200 * education + np.random.normal(0, 500, n)
# 创建DataFrame
socioeconomic_data = pd.DataFrame(
{"教育水平": education, "收入_元每月": income, "消费水平": consumption}
)
print("=== 简单相关分析 ===")
simple_corr, simple_p = stats.pearsonr(
socioeconomic_data["教育水平"], socioeconomic_data["消费水平"]
)
print(f"教育水平与消费水平的简单相关系数: {simple_corr:.3f}")
print("\n=== 偏相关分析(控制收入)===")
# 使用pingouin进行偏相关分析
partial_corr = pg.partial_corr(
data=socioeconomic_data, x="教育水平", y="消费水平", covar="收入_元每月"
)
print(partial_corr.round(3))
# 运行结果:
'''
=== 简单相关分析 ===
教育水平与消费水平的简单相关系数: 0.965
=== 偏相关分析(控制收入)===
n r CI95% p-val
pearson 100 0.129 [-0.07, 0.32] 0.204
'''
</code></pre>
<p>图形化结果如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/83005/202512/83005-20251219160453602-2046001280.png" alt="" loading="lazy"></p>
<h1 id="4-距离相关分析多变量关系的度量">4. 距离相关分析:多变量关系的度量</h1>
<p><strong>距离相关分析</strong>可以衡量两组变量(每个变量组包含多个指标)之间的相关性,是<strong>多变量分析</strong>的有力工具。</p>
<p>比如<strong>压力与工作效率</strong>(耶克斯-多德森定律)的关系。</p>
<p>压力太小,人会懒散(效率低);压力太大,人会崩溃(效率低);只有适度的压力,效率最高。</p>
<p>这是一个 <strong>U型(非线性)</strong> 关系。这时候用<code>Pearson</code>去算,结果可能是<code>0</code>(因为它找不到直线),但其实它们关系很紧密。</p>
<p>下面的示例,比较两个城市的综合发展水平(经济、环境、社会等多维度指标)。</p>
<pre><code class="language-python">from scipy.spatial.distance import pdist, squareform
# 模拟数据:两个城市的多维指标
np.random.seed(42)
# 城市A和城市B的6个发展指标(经济、环境、教育、医疗、文化、创新)
indicators = ["经济", "环境", "教育", "医疗", "文化", "创新"]
city_a = np.array() + np.random.normal(0, 5, 6)
city_b = np.array() + np.random.normal(0, 5, 6)
# 创建多个城市的比较数据
cities_data = pd.DataFrame(
{
"城市A": city_a,
"城市B": city_b,
"城市C": np.array() + np.random.normal(0, 5, 6),
"城市D": np.array() + np.random.normal(0, 5, 6),
"城市E": np.array() + np.random.normal(0, 5, 6),
},
index=indicators,
)
print("各城市发展指标数据:")
print(cities_data.round(2))
# 计算距离相关性(自定义简化版)
def distance_correlation(x, y):
"""计算距离相关性"""
# 计算距离矩阵
def dist_matrix(v):
v = np.array(v)
n = len(v)
a = np.zeros((n, n))
for i in range(n):
for j in range(n):
a = abs(v - v)
return a
A = dist_matrix(x)
B = dist_matrix(y)
# 双中心化
def double_centering(D):
n = len(D)
row_means = D.mean(axis=1)
col_means = D.mean(axis=0)
grand_mean = D.mean()
C = np.zeros((n, n))
for i in range(n):
for j in range(n):
C = D - row_means - col_means + grand_mean
return C
A_centered = double_centering(A)
B_centered = double_centering(B)
# 计算距离协方差和距离方差
dCov_XY = np.sqrt((A_centered * B_centered).sum() / (len(x) ** 2))
dVar_X = np.sqrt((A_centered * A_centered).sum() / (len(x) ** 2))
dVar_Y = np.sqrt((B_centered * B_centered).sum() / (len(x) ** 2))
# 计算距离相关性
dCor = dCov_XY / np.sqrt(dVar_X * dVar_Y)
return dCor
# 比较城市A和城市B的距离相关性
city_a_scores = cities_data["城市A"].values
city_b_scores = cities_data["城市B"].values
dcor = distance_correlation(city_a_scores, city_b_scores)
print(f"\n城市A与城市B的距离相关性: {dcor:.3f}")
# 运行结果:
'''
各城市发展指标数据:
城市A城市B城市C城市D 城市E
经济87.4895.9071.2187.4675.28
环境77.3178.8475.4362.9488.55
教育93.2484.6566.3892.3374.25
医疗95.6294.7177.1985.8786.88
文化80.8376.6884.9476.3484.00
创新78.8382.6773.5783.8876.54
城市A与城市B的距离相关性: 0.764
'''
</code></pre>
<p>图形化的结果如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/83005/202512/83005-20251219160453535-581787435.png" alt="" loading="lazy"></p>
<h1 id="5-相关性卡方检验分类变量的关联分析">5. 相关性卡方检验:分类变量的关联分析</h1>
<p>如果我们分析的数据不是数字,而是<strong>类别</strong>(定类数据/低测度数据)呢?</p>
<p>当两个变量都是分类变量(定类数据)时,我们可以使用<strong>卡方检验</strong>来分析它们之间是否存在关联。</p>
<p>比如<strong>性别(男/女)</strong> 与 <strong>爱喝的饮料(奶茶/咖啡)</strong> 是否相关?<br>
这里没有大小之分,只有类别。我们可以使用<strong>卡方检验</strong>来判断两个分类变量是否独立。</p>
<p>下面的示例,我们尝试分析性别与购物偏好类别之间的关系。</p>
<pre><code class="language-python"># 模拟数据:性别与购物偏好
np.random.seed(42)
# 创建列联表
data = pd.DataFrame(
{
"性别": np.random.choice(["男", "女"], 200),
"购物偏好": np.random.choice(
["电子产品", "服装鞋包", "美妆护肤", "运动户外", "家居生活"], 200
),
}
)
# 根据性别调整偏好概率(创建一些关联)
for i in range(len(data)):
if data.loc == "男":
# 男性更可能偏好电子产品和运动户外
if np.random.random() < 0.3:
data.loc = "电子产品"
elif np.random.random() < 0.5:
data.loc = "运动户外"
else:
# 女性更可能偏好美妆护肤和服装鞋包
if np.random.random() < 0.3:
data.loc = "美妆护肤"
elif np.random.random() < 0.4:
data.loc = "服装鞋包"
# 创建列联表
contingency_table = pd.crosstab(data["性别"], data["购物偏好"])
print("性别与购物偏好的列联表:")
print(contingency_table)
# 执行卡方检验
chi2, p, dof, expected = stats.chi2_contingency(contingency_table)
print(f"\n卡方检验结果:")
print(f"卡方值: {chi2:.3f}")
print(f"P值: {p:.5f}")
print(f"自由度: {dof}")
print(f"\n期望频数表:")
print(
pd.DataFrame(
expected, index=contingency_table.index, columns=contingency_table.columns
).round(2)
)
# 计算Cramer's V(衡量分类变量关联强度)
def cramers_v(contingency_table):
"""计算Cramer's V系数"""
chi2 = stats.chi2_contingency(contingency_table)
n = contingency_table.sum().sum()
min_dim = min(contingency_table.shape) - 1
v = np.sqrt(chi2 / (n * min_dim))
return v
cramers_v_value = cramers_v(contingency_table)
print(f"\nCramer's V系数: {cramers_v_value:.3f}")
print("解读:0.1-0.3弱相关,0.3-0.5中等相关,>0.5强相关")
# 运行结果:
'''
性别与购物偏好的列联表:
购物偏好家居生活服装鞋包电子产品美妆护肤运动户外
性别
女 2 33 15 40 10
男 10 2 45 10 33
卡方检验结果:
卡方值: 78.093
P值: 0.00000
自由度: 4
期望频数表:
购物偏好家居生活服装鞋包电子产品美妆护肤运动户外
性别
女 6.017.530.025.021.5
男 6.017.530.025.021.5
Cramer's V系数: 0.625
解读:0.1-0.3弱相关,0.3-0.5中等相关,>0.5强相关
'''
</code></pre>
<p>图形化的结果如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/83005/202512/83005-20251219160453536-166297866.png" alt="" loading="lazy"></p>
<h1 id="6-总结">6. 总结</h1>
<p>数据分析师在面对变量关系时,要根据数据的 <strong>“长相”</strong> 来选工具:</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>适用数据类型</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>皮尔森相关</td>
<td>连续、正态分布、线性关系</td>
<td>最常用,对异常值敏感</td>
</tr>
<tr>
<td>斯皮尔曼相关</td>
<td>连续但不正态,或有序数据</td>
<td>稳健,适用于单调关系</td>
</tr>
<tr>
<td>肯德尔相关</td>
<td>有序数据,小样本</td>
<td>适合等级数据,解释直观</td>
</tr>
<tr>
<td>偏相关</td>
<td>需控制其他变量影响</td>
<td>揭示变量间的直接关系</td>
</tr>
<tr>
<td>距离相关</td>
<td>多变量组间关系</td>
<td>衡量多维度综合关联</td>
</tr>
<tr>
<td>卡方检验</td>
<td>分类变量</td>
<td>检验类别间关联性</td>
</tr>
</tbody>
</table>
<p>我们在分析数据相关性的时候,不要急于得出数据之间是否相关的结论。</p>
<p>先看看下面的注意事项是否有违背!</p>
<ol>
<li><strong>相关性不等于因果性</strong>:即使两个变量高度相关,也不能断定一个导致另一个</li>
<li><strong>警惕第三变量问题</strong>:可能两个变量都受到第三个未测量变量的影响</li>
<li><strong>注意异常值的影响</strong>:异常值可能夸大或掩盖真实的相关性</li>
<li><strong>检查线性假设</strong>:皮尔森相关要求线性关系,非线性关系需要其他方法</li>
<li><strong>样本大小的重要性</strong>:小样本的相关性可能不稳定</li>
</ol><br><br>
来源:https://www.cnblogs.com/wang_yb/p/19372396 哇,写的太棒了!终于有人把相关性分析讲得这么通俗易懂了!
以前我每次看到老板问“这两个数据有没有关系”的时候,都是凭感觉瞎猜,看完这篇终于知道该用什么工具了。
特别点赞那个冰淇淋和溺水的例子,太形象了!以前我确实经常忽略“第三者”变量的问题,以后分析数据可得小心点了。
不过有个小问题想请教一下:
文章里提到皮尔森相关要求数据服从正态分布,但实际工作中样本量很小的时候,怎么判断数据是否正态啊?有没有简单实用的判断方法?
还有那个距离相关性,看起来很强大,但感觉代码实现起来有点复杂,实际工作中用得多吗?
总之感谢楼主的无私分享,收藏了!希望以后能看到更多这种实战型的数据分析教程~
https://example.com/点赞.png 给楼主打call!
頁:
[1]