ARTICLE DETAIL

资讯详情

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

数据分析入门:皮尔逊、斯皮尔曼、肯德尔三大相关系数详解与Python实战

数据分析入门:皮尔逊、斯皮尔曼、肯德尔三大相关系数详解与Python实战 1. 项目概述从“相关”到“因果”的桥梁做数据分析、搞量化研究甚至是写论文你是不是经常听到“这两个变量有关系”的说法但“有关系”三个字太模糊了是强是弱是正还是负这种关系可靠吗这时候相关系数就是你手中那把最趁手的“尺子”它能把你直觉上的“感觉有关系”变成一个精确的、可比较的数字。我做了十多年数据相关的工作从金融风控到用户行为分析可以说相关系数是入门数据分析必须跨过的第一道坎也是后续所有复杂模型比如回归分析的基石。很多人学相关系数容易陷入两个极端要么一头扎进公式推导里出不来觉得枯燥又难懂要么只记几个函数名跑出结果却不知道怎么解读甚至用错场合。这篇笔记我就想用最“人话”的方式帮你把理论和实操彻底打通。我们不光要搞清楚皮尔逊、斯皮尔曼这些系数到底在算什么更要弄明白什么时候该用谁跑出来的结果怎么看以及那些新手最容易踩的“坑”在哪里。目标很简单让你看完就能用用了不出错真正理解数据背后“关系”的度量逻辑。2. 相关系数核心理论不止是“一个数字”在动手写代码之前我们必须把地基打牢。相关系数不是一个单一的概念而是一个“家族”针对不同类型的数据和关系模式我们有不同的工具。用错了工具就像用螺丝刀去敲钉子费力不讨好结论还可能全错。2.1 皮尔逊相关系数线性关系的“黄金标准”当我们谈论“相关系数”而没加任何定语时通常指的就是皮尔逊积矩相关系数。它的核心任务是衡量两个连续型数值变量之间线性关系的强度和方向。它的计算思想非常直观想象我们把两个变量分别标准化减去均值除以标准差变成均值为0、标准差为1的“标准分”。然后计算每一对标准分的乘积再求平均。这个平均值就是皮尔逊相关系数r。如果r 1意味着所有点都完美地落在一条斜向上的直线上一个变量增加另一个变量也严格按比例增加。如果r -1则是完美斜向下的直线一个增加另一个严格按比例减少。如果r 0则说明不存在线性关系但注意可能存在其他复杂关系如曲线关系。公式背后的逻辑r Cov(X, Y) / (σ_X * σ_Y)。分子是协方差衡量两个变量如何共同变化分母是两个标准差的乘积目的是消除量纲影响将系数规范到 [-1, 1] 区间内便于比较。注意皮尔逊相关系数有严格的适用前提1变量是连续或近似连续的2关系是线性的3数据最好接近正态分布4没有明显的异常值。违反这些前提r值可能会产生严重误导。2.2 斯皮尔曼等级相关系数单调关系的“抗干扰能手”现实数据往往没那么“规矩”。数据可能不是正态分布或者存在一些异常值又或者我们关心的仅仅是“当一个变量变大时另一个变量是否也倾向于变大或变小”这种更广义的单调关系而不一定是严格的直线关系。这时斯皮尔曼相关系数就派上用场了。它的聪明之处在于“降维打击”它不直接使用变量的原始值而是将每个变量的数据分别从小到大排序赋予其秩次即排名第1名、第2名...。然后计算这两个“排名”序列的皮尔逊相关系数。因为排名数据对异常值不敏感一个极大的异常值也只是排名第一而已并且不要求数据满足正态分布所以斯皮尔曼系数的稳健性更强。适用场景数据是顺序尺度的例如满意度调查的“非常不满意、不满意、一般、满意、非常满意”。数据分布未知或明显非正态。怀疑存在单调但非线性的关系例如指数增长趋势。数据中存在异常值。2.3 肯德尔等级相关系数一致性的“精细评判员”肯德尔系数同样用于衡量两个等级变量之间的相关性但它的计算逻辑与斯皮尔曼不同。它考察的是所有可能的数据对中一致对和不一致对的比例。什么是“一致对”取两对观测值(x_i, y_i)和(x_j, y_j)如果(x_i - x_j)和(y_i - y_j)同号即x变大时y也变大或x变小时y也变小它们就是一致对。反之则为不一致对。肯德尔系数τ就是一致对数目 - 不一致对数目除以总的对数。它的解释更侧重于概率如果τ 0.8可以理解为随机抽取两个样本它们排名一致的可能性比不一致的可能性高80%。与斯皮尔曼的细微差别当数据量较小时肯德尔系数通常更精确。此外它对“打结”数据即存在相同排名的情况有更成熟的处理方式。在样本量巨大时两者结论通常一致。2.4 其他相关系数速览除了上述三大主力根据数据类型还有更多选择点二列相关用于衡量一个真正二分类变量如男/女是/否和一个连续变量如考试成绩之间的相关。Φ系数用于衡量两个真正二分类变量之间的相关如性别与是否通过考试。偏相关与半偏相关当我们想研究两个变量之间的关系但又想控制住第三个或多个变量的影响时使用。例如研究学习时间和成绩的关系需要控制“智商”这个变量带来的影响此时就需要计算偏相关系数。3. 相关系数的实战计算与解读理论懂了我们就要上手算。这里我会用 Python 的pandas,numpy,scipy和seaborn库来演示这也是实际工作中最高效的组合。3.1 数据准备与探索性可视化在计算任何系数之前画图是必不可少的第一步。可视化能帮你直观感受关系形态避免盲目计算。import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt from scipy import stats # 设置中文显示和图形样式可选 plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False sns.set_style(whitegrid) # 示例创建一个包含多种关系的数据集 np.random.seed(42) n 100 data pd.DataFrame({ 线性正相关: np.random.randn(n) * 2 np.arange(n) * 0.1, # 线性增长 线性负相关: -np.arange(n) * 0.1 np.random.randn(n) * 2, 曲线相关: np.sin(np.linspace(0, 4*np.pi, n)) * 10 np.random.randn(n) * 2, # 正弦曲线 无相关: np.random.randn(n) * 5, 异常值影响: np.concatenate([np.random.randn(n-1), [50]]) # 最后一个点是异常值 }) data[异常值影响_线性] data[异常值影响] * 0.5 np.random.randn(n) * 2 # 绘制散点图矩阵 sns.pairplot(data[[线性正相关, 线性负相关, 曲线相关, 无相关]]) plt.suptitle(不同关系模式的散点图矩阵, y1.02) plt.show() # 单独绘制带有异常值的散点图 plt.figure(figsize(6,4)) plt.scatter(data[异常值影响], data[异常值影响_线性], alpha0.7) plt.xlabel(异常值影响) plt.ylabel(异常值影响_线性) plt.title(包含极端异常值的散点图) plt.show()通过散点图你可以一眼看出“线性正相关”和“线性负相关”大致呈直线“曲线相关”呈波浪形“无相关”则是一团乱麻而“异常值影响”图中一个孤立的点会极大地扭曲我们对整体趋势的判断。3.2 皮尔逊相关系数的计算与假设检验计算相关系数本身很简单但关键在于解读和检验。# 计算皮尔逊相关系数矩阵 pearson_corr data[[线性正相关, 线性负相关, 曲线相关, 无相关]].corr(methodpearson) print(皮尔逊相关系数矩阵\n, pearson_corr) # 使用scipy进行带p值的相关系数计算 # 以‘线性正相关’和‘线性负相关’为例 r_value, p_value stats.pearsonr(data[线性正相关], data[线性负相关]) print(f\n‘线性正相关’与‘线性负相关’的皮尔逊相关分析) print(f 相关系数 r {r_value:.4f}) print(f P值 {p_value:.4e}) # 对‘曲线相关’和‘无相关’也进行计算 r_curve, p_curve stats.pearsonr(data[曲线相关], data[无相关]) print(f\n‘曲线相关’与‘无相关’的皮尔逊相关分析) print(f 相关系数 r {r_curve:.4f}) print(f P值 {p_curve:.4e})解读要点系数值r_value会接近 -1证实了强烈的线性负相关。r_curve会接近0因为正弦曲线与随机噪声之间没有线性关系。P值这是显著性检验的结果。原假设是“两个变量总体相关系数为0即无关”。通常如果p_value 0.05或更严格的0.01我们就有足够的统计学证据拒绝原假设认为观察到的相关关系不是偶然产生的。上面例子中第一组的p值会极小如 0.001第二组的p值会大于0.05。注意陷阱对于“曲线相关”变量自身如果我们计算它与一个线性递增序列的相关系数r可能也会很小但这绝不意味着它们没关系这就是为什么必须先看图。3.3 斯皮尔曼与肯德尔系数的计算当数据不满足皮尔逊的前提时我们转向等级相关。# 计算斯皮尔曼和肯德尔相关系数 # 以‘曲线相关’和‘无相关’为例同时演示带异常值的数据 spearman_corr, spearman_p stats.spearmanr(data[曲线相关], data[无相关]) kendall_corr, kendall_p stats.kendalltau(data[曲线相关], data[无相关]) print(f斯皮尔曼相关系数: {spearman_corr:.4f}, P值: {spearman_p:.4e}) print(f肯德尔相关系数: {kendall_corr:.4f}, P值: {kendall_p:.4e}) # 演示异常值的影响对比皮尔逊和斯皮尔曼 r_pearson_outlier, p_pearson stats.pearsonr(data[异常值影响], data[异常值影响_线性]) r_spearman_outlier, p_spearman stats.spearmanr(data[异常值影响], data[异常值影响_线性]) print(f\n【异常值场景对比】) print(f皮尔逊相关系数: {r_pearson_outlier:.4f} (被异常值严重扭曲)) print(f斯皮尔曼相关系数: {r_spearman_outlier:.4f} (相对稳健))这个对比会清晰地展示一个极端异常值如何将皮尔逊系数拉向一个失真的方向可能变得很高或很低而斯皮尔曼系数由于其基于排名的特性受此影响要小得多。3.4 相关系数矩阵与热力图在实际项目中我们经常需要一次性考察多个变量两两之间的关系。相关系数矩阵和热力图是最佳工具。# 计算所有数值列之间的混合相关系数矩阵以斯皮尔曼为例 corr_matrix data.corr(methodspearman) print(斯皮尔曼相关系数矩阵\n, corr_matrix) # 绘制热力图 plt.figure(figsize(10, 8)) # 使用mask隐藏上三角部分可选因为矩阵是对称的 mask np.triu(np.ones_like(corr_matrix, dtypebool)) sns.heatmap(corr_matrix, maskmask, annotTrue, fmt.2f, cmapRdBu_r, center0, squareTrue, linewidths.5, cbar_kws{shrink: .8}) plt.title(变量间斯皮尔曼相关系数热力图) plt.tight_layout() plt.show()热力图中颜色越深红色表示正相关越强颜色越浅蓝色表示负相关越强。annotTrue将数值显示在格子中fmt.2f控制显示两位小数。这张图能让你快速锁定哪些变量间存在强相关关系为后续分析如特征选择、共线性排查提供关键依据。4. 深入实操从计算到洞察的完整案例现在我们用一个更贴近实际的案例串联起从数据清洗、可视化、系数计算到结果解读的全过程。假设我们有一份某电商平台的用户行为数据集user_behavior.csv包含以下字段用户ID年龄年收入万每周浏览时长小时平均订单金额元月度购买频率满意度评分1-5。4.1 案例背景与业务问题业务方想知道用户的“浏览时长”和“购买频率”是否相关投入资源增加用户浏览时间是否能促进复购“年收入”和“平均订单金额”是什么关系高收入用户是否倾向于单次购买更贵的商品“满意度评分”与其他行为指标的关系如何满意的用户是否有特定的行为模式4.2 数据清洗与预处理# 1. 加载数据 df pd.read_csv(user_behavior.csv) print(数据形状:, df.shape) print(数据前5行:\n, df.head()) print(\n数据基本信息:) print(df.info()) print(\n描述性统计:) print(df.describe()) # 2. 处理缺失值与异常值 # 检查缺失 print(f缺失值统计:\n{df.isnull().sum()}) # 简单处理对于数值列用中位数填充对于满意度评分用众数填充假设为整数 if df.isnull().any().any(): for col in df.select_dtypes(include[np.number]).columns: if df[col].isnull().sum() 0: if col 满意度评分1-5: fill_value df[col].mode()[0] # 众数 else: fill_value df[col].median() # 中位数 df[col].fillna(fill_value, inplaceTrue) print(f列 {col} 的缺失值已用 {fill_value} 填充。) # 检查异常值使用箱线图或IQR法则 plt.figure(figsize(12, 6)) df_boxplot df[[年龄, 年收入万, 每周浏览时长小时, 平均订单金额元]] df_boxplot.boxplot() plt.xticks(rotation45) plt.title(数值变量箱线图检查异常值) plt.tight_layout() plt.show() # 基于业务逻辑处理异常值例如年龄100或10的视为异常浏览时长每周168小时7*24不可能 df df[(df[年龄] 18) (df[年龄] 80)] df df[df[每周浏览时长小时] 100] # 假设每周浏览超过100小时为异常 print(f清理异常值后数据形状: {df.shape})4.3 多方法相关系数计算与对比分析针对不同的业务问题选择合适的相关系数。# 选择需要分析的数值列 analysis_cols [年龄, 年收入万, 每周浏览时长小时, 平均订单金额元, 月度购买频率, 满意度评分1-5] analysis_df df[analysis_cols] # 计算三种相关系数矩阵 pearson_matrix analysis_df.corr(methodpearson) spearman_matrix analysis_df.corr(methodspearman) # pandas 没有内置的kendall我们用scipy循环计算或使用.corr(kendall’)如果版本支持 # 这里为演示先计算斯皮尔曼热力图肯德尔可以类似进行重点配对分析 fig, axes plt.subplots(1, 2, figsize(16, 6)) # 皮尔逊热力图 sns.heatmap(pearson_matrix, annotTrue, fmt.2f, cmapRdBu_r, center0, squareTrue, axaxes[0]) axes[0].set_title(皮尔逊相关系数矩阵) # 斯皮尔曼热力图 sns.heatmap(spearman_matrix, annotTrue, fmt.2f, cmapRdBu_r, center0, squareTrue, axaxes[1]) axes[1].set_title(斯皮尔曼相关系数矩阵) plt.tight_layout() plt.show() # 针对具体业务问题进行详细的配对分析和检验 print(\n 业务问题1: ‘浏览时长’ vs ‘购买频率’ ) r_pearson_1, p_pearson_1 stats.pearsonr(df[每周浏览时长小时], df[月度购买频率]) r_spearman_1, p_spearman_1 stats.spearmanr(df[每周浏览时长小时], df[月度购买频率]) print(f皮尔逊: r {r_pearson_1:.3f}, p {p_pearson_1:.4f}) print(f斯皮尔曼: ρ {r_spearman_1:.3f}, p {p_spearman_1:.4f}) # 绘制散点图与回归线 sns.jointplot(datadf, x每周浏览时长小时, y月度购买频率, kindreg, height6) plt.suptitle(浏览时长与购买频率关系含回归线, y1.02) plt.show() print(\n 业务问题2: ‘年收入’ vs ‘平均订单金额’ ) # 先看分布 fig, (ax1, ax2) plt.subplots(1, 2, figsize(12,4)) sns.histplot(df[年收入万], kdeTrue, axax1) ax1.set_title(年收入分布) sns.histplot(df[平均订单金额元], kdeTrue, axax2) ax2.set_title(平均订单金额分布) plt.tight_layout() plt.show() # 计算相关系数 r_pearson_2, p_pearson_2 stats.pearsonr(df[年收入万], df[平均订单金额元]) r_spearman_2, p_spearman_2 stats.spearmanr(df[年收入万], df[平均订单金额元]) print(f皮尔逊: r {r_pearson_2:.3f}, p {p_pearman_2:.4f}) print(f斯皮尔曼: ρ {r_spearman_2:.3f}, p {p_spearman_2:.4f}) print(\n 业务问题3: ‘满意度评分’与其他指标 ) # 满意度是1-5的等级数据更适合用斯皮尔曼或肯德尔 sat_corr_with {} for col in [每周浏览时长小时, 平均订单金额元, 月度购买频率]: rho, p stats.spearmanr(df[满意度评分1-5], df[col]) sat_corr_with[col] {相关系数: rho, P值: p} print(f满意度 vs {col}: ρ {rho:.3f}, p {p:.4f})4.4 结果解读与业务报告撰写要点根据上面的输出我们可以形成如下分析结论浏览时长与购买频率斯皮尔曼相关系数约为0.65(p0.001)显示存在较强的正相关关系。业务启示鼓励用户增加浏览时长很可能对提升复购率有积极影响。但需注意这是相关关系不一定是因果关系。可能需要通过A/B测试进一步验证。年收入与平均订单金额皮尔逊和斯皮尔曼系数都显示较弱的相关性例如0.15左右。业务启示高收入用户并不一定会在单次消费中花更多钱。提升客单价可能需要从商品推荐、促销策略等其他方面入手而非单纯瞄准高收入群体。满意度评分可能与“平均订单金额”有微弱的正相关与“购买频率”相关度可能更高。业务启示提升用户满意度可能对促进用户多次购买提高忠诚度的效果比促进单次消费金额更明显。实操心得在撰写报告时不要只扔出一个相关系数。一定要伴随三样东西可视化图形散点图、显著性P值、业务层面的解读。同时必须明确指出所使用的相关系数类型及其前提假设这体现了分析的专业性。5. 高级话题与常见陷阱规避掌握了基础计算和解读我们还需要了解一些高级用法和深坑才能让分析更严谨。5.1 偏相关分析剥离混淆因素的影响很多时候两个变量之间的相关可能是由第三个变量混淆变量共同作用造成的假象。例如我们发现“冰淇淋销量”和“溺水人数”高度正相关。但这显然不是因果关系而是因为它们都受“季节温度”影响。要探究“冰淇淋销量”和“溺水人数”的真实关系就需要“控制”温度的影响计算偏相关系数。# 使用 pingouin 库进行偏相关分析 (更便捷) # 假设我们想探究‘浏览时长’和‘购买频率’的关系同时控制‘年龄’和‘年收入’的影响 # 首先安装: pip install pingouin import pingouin as pg # 假设 df 中包含 每周浏览时长小时, 月度购买频率, 年龄, 年收入万 partial_corr pg.partial_corr(datadf, x每周浏览时长小时, y月度购买频率, covar[年龄, 年收入万], methodpearson) print(偏相关分析结果控制年龄和年收入) print(partial_corr)如果偏相关系数相比于原来的简单相关系数大幅减小甚至不再显著那就说明原先的相关很可能是由被控制的变量年龄、收入所驱动的。5.2 相关系数的置信区间相关系数是一个点估计我们还应报告其估计的不确定性即置信区间。# 计算皮尔逊相关系数的95%置信区间 def pearson_ci(x, y, alpha0.05): r, p stats.pearsonr(x, y) n len(x) # 使用Fisher z变换 z np.arctanh(r) se 1 / np.sqrt(n - 3) z_crit stats.norm.ppf(1 - alpha/2) lo_z, hi_z z - z_crit*se, z z_crit*se lo, hi np.tanh((lo_z, hi_z)) return r, (lo, hi) r, ci pearson_ci(df[每周浏览时长小时], df[月度购买频率]) print(f浏览时长与购买频率的皮尔逊相关系数: {r:.3f}) print(f95% 置信区间: [{ci[0]:.3f}, {ci[1]:.3f}])报告置信区间例如0.58 [0.50, 0.65]比单纯报告一个点估计值0.58包含更多信息它给出了系数可能波动的范围。5.3 新手最常踩的五大坑混淆相关与因果这是最经典、最严重的错误。A和B相关不代表A导致B。可能B导致A或者C同时导致A和B。永远记住相关不等于因果。确立因果需要更严谨的实验设计如随机对照试验。忽视线性假设皮尔逊系数只度量线性关系。对于像y x^2这样的曲线关系皮尔逊r可能接近0但显然两者存在确定性的关系。一定要先画散点图。忽略异常值一个极端的异常值可以完全扭曲皮尔逊相关系数使其失去代表性。在计算前必须通过可视化如散点图、箱线图检查数据并考虑使用斯皮尔曼等稳健方法或对异常值进行合理处理。基于小样本得出强结论样本量过小时即使计算出一个很大的r值如0.9也可能由于随机性导致P值可能不显著。务必报告P值或置信区间并结合样本量谨慎解读。误用相关系数比较相关强度比较不同变量对之间的相关系数大小时要小心。例如r0.8不一定比r0.5的关系“强”一倍。相关系数不是等距尺度。更重要的是关系的稳定性置信区间宽度和显著性P值同样重要。5.4 自动化分析脚本模板将常用分析流程封装成函数可以极大提高效率。def comprehensive_correlation_analysis(df, var1, var2, methodauto, plotTrue, alpha0.05): 综合相关分析函数 参数: df: DataFrame var1, var2: 要分析的两个变量名 method: pearson, spearman, kendall, 或 auto自动根据数据特性选择 plot: 是否绘制图形 alpha: 显著性水平 返回: 包含详细结果的字典 x df[var1].dropna() y df[var2].dropna() # 确保x和y长度一致 common_index x.index.intersection(y.index) x, y x.loc[common_index], y.loc[common_index] # 自动选择方法 if method auto: # 简单启发式如果数据是明确的等级或非正态用斯皮尔曼 if df[var1].nunique() 10 or df[var2].nunique() 10: # 类别较少 method spearman else: # 可以添加更正式的正态性检验如Shapiro-Wilk method pearson results {变量对: f{var1} vs {var2}, 方法: method} # 计算相关系数及P值 if method pearson: r, p stats.pearsonr(x, y) # 计算置信区间 z np.arctanh(r) se 1 / np.sqrt(len(x) - 3) z_crit stats.norm.ppf(1 - alpha/2) ci_low, ci_high np.tanh([z - z_crit*se, z z_crit*se]) elif method spearman: r, p stats.spearmanr(x, y) # 斯皮尔曼的CI计算较复杂此处省略或使用bootstrap ci_low, ci_high np.nan, np.nan elif method kendall: r, p stats.kendalltau(x, y) ci_low, ci_high np.nan, np.nan else: raise ValueError(method 必须是 pearson, spearman, kendall 或 auto) results[相关系数] r results[P值] p results[f{int((1-alpha)*100)}% 置信区间] (ci_low, ci_high) results[显著性] 显著 if p alpha else 不显著 # 绘图 if plot: fig, axes plt.subplots(1, 2, figsize(12, 4)) # 散点图与回归线 sns.regplot(xx, yy, axaxes[0], scatter_kws{alpha:0.5}) axes[0].set_xlabel(var1) axes[0].set_ylabel(var2) axes[0].set_title(f{var1} vs {var2} 散点图与回归线\n{method} r {r:.3f} (p{p:.4f})) # 残差图检查线性假设仅对pearson有意义 if method pearson: from sklearn.linear_model import LinearRegression model LinearRegression().fit(x.values.reshape(-1,1), y) y_pred model.predict(x.values.reshape(-1,1)) residuals y - y_pred axes[1].scatter(y_pred, residuals, alpha0.5) axes[1].axhline(y0, colorr, linestyle--) axes[1].set_xlabel(预测值) axes[1].set_ylabel(残差) axes[1].set_title(残差图检查线性、同方差性) else: axes[1].text(0.5, 0.5, f使用 {method} 相关系数\n无需检查线性假设, hacenter, vacenter, transformaxes[1].transAxes) axes[1].set_title(等级相关无需线性假设) plt.tight_layout() plt.show() return results # 使用示例 result comprehensive_correlation_analysis(df, 每周浏览时长小时, 月度购买频率, methodauto) print(result)掌握相关系数远不止学会调用一个corr()函数。它关乎如何正确地提出业务问题如何严谨地选择度量工具如何审慎地解读数据给出的答案以及如何清晰地呈现分析结果。从看到“相关”二字就发怵到能游刃有余地运用这把尺子去丈量变量间的关系这个过程本身就是数据分析思维的一次重要升级。多练多看多思考最重要的是永远对数据保持一份敬畏和怀疑。
返回列表