别再只用皮尔逊了!用Python的Scipy.stats搞定斯皮尔曼相关系数(含P值解读)
别再只用皮尔逊了用Python的Scipy.stats搞定斯皮尔曼相关系数含P值解读在数据分析的世界里皮尔逊相关系数就像是一把瑞士军刀被广泛使用却不一定适合所有场景。当数据呈现出非线性关系、不符合正态分布或是属于定序尺度时斯皮尔曼相关系数才是更合适的选择。本文将带你深入了解何时使用斯皮尔曼相关系数以及如何用Python的scipy.stats模块轻松实现它并正确解读结果。1. 为什么需要斯皮尔曼相关系数皮尔逊相关系数衡量的是两个变量之间的线性关系但它有三个严格的假设条件变量间存在线性关系数据是连续且正态分布的变量间没有异常值然而现实世界的数据往往不满足这些条件。例如用户满意度调查1-5分的等级数据产品排名数据收入与幸福感的关系通常是非线性的斯皮尔曼相关系数的优势在于不要求数据满足正态分布能够捕捉单调关系不一定是线性适用于定序数据如排名、等级提示单调关系指的是当一个变量增加时另一个变量始终保持增加或减少的趋势但不一定是直线关系。2. 斯皮尔曼相关系数的数学原理斯皮尔曼相关系数实际上是皮尔逊相关系数在变量秩上的应用。计算步骤如下对每个变量的观测值进行排序得到秩rank计算两个变量秩之间的皮尔逊相关系数数学公式为 ρ 1 - (6∑d²)/(n(n²-1))其中d是两个变量的秩差n是样本量关键特性取值范围在-1到1之间1表示完全单调递增关系-1表示完全单调递减关系0表示没有单调关系3. Python实现scipy.stats.spearmanrPython的scipy.stats模块提供了简单易用的spearmanr函数。让我们通过几个实际例子来学习如何使用它。3.1 基本用法import numpy as np from scipy import stats # 示例数据 x [1, 2, 3, 4, 5, 6, 7, 8, 9] y [2, 1, 2, 4.5, 7, 6.5, 6, 9, 9.5] # 计算斯皮尔曼相关系数和p值 corr, p_value stats.spearmanr(x, y) print(f斯皮尔曼相关系数: {corr:.3f}) print(fP值: {p_value:.4f})输出结果解读相关系数接近1表明x和y之间存在强单调递增关系P值小于0.05说明这种相关性在统计上是显著的3.2 处理有相同值的数据当数据中存在相同值时spearmanr会自动处理秩的平均分配x [1, 2, 2, 3, 3, 3, 4, 5] y [5, 4, 4, 3, 2, 2, 1, 1] corr, p_value stats.spearmanr(x, y) print(f相关系数: {corr:.3f}) # 输出约为-0.9763.3 二维数组的计算对于二维数组spearmanr会计算所有列之间的相关系数矩阵data np.array([ [1, 5, 9], [2, 4, 8], [3, 3, 7], [4, 2, 6], [5, 1, 5] ]) corr_matrix, p_matrix stats.spearmanr(data, axis0) print(相关系数矩阵:) print(corr_matrix)4. 结果解读与常见误区正确解读斯皮尔曼相关系数的结果至关重要以下是几个关键点4.1 相关系数的含义相关系数范围关系强度解释0.00-0.19非常弱0.20-0.39弱0.40-0.59中等0.60-0.79强0.80-1.00非常强4.2 P值的解读P值用于检验相关系数是否显著不为零。通常P值 0.05相关性统计显著P值 ≥ 0.05不能拒绝无相关性的原假设常见误区认为高相关系数就意味着因果关系忽略P值只看相关系数大小对非单调关系使用斯皮尔曼相关系数4.3 与皮尔逊系数的对比让我们通过一个例子比较两种方法import matplotlib.pyplot as plt # 生成非线性但单调的数据 x np.linspace(1, 10, 100) y np.exp(x/3) np.random.normal(0, 1, 100) # 计算两种相关系数 pearson_corr stats.pearsonr(x, y)[0] spearman_corr stats.spearmanr(x, y)[0] plt.scatter(x, y) plt.title(f皮尔逊: {pearson_corr:.2f}, 斯皮尔曼: {spearman_corr:.2f}) plt.show()在这个例子中皮尔逊系数可能会低估实际关系的强度而斯皮尔曼系数能更好地捕捉这种单调但非线性的关系。5. 实际应用案例5.1 用户满意度分析假设我们收集了某产品的用户评分和回购意愿数据# 1-5分评分1为非常不满意5为非常满意 satisfaction [3, 4, 2, 5, 4, 3, 2, 1, 4, 5] # 1-3分1为不会回购3为肯定会回购 repurchase [2, 3, 1, 3, 2, 2, 1, 1, 3, 3] corr, p stats.spearmanr(satisfaction, repurchase) print(f满意度与回购意愿的斯皮尔曼相关系数: {corr:.3f}, P值: {p:.4f})5.2 运动员排名比较比较两位体育评论员对10位网球运动员的排名# 评论员A和B对10位运动员的排名1最好10最差 ranker_A [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ranker_B [2, 1, 4, 3, 6, 5, 8, 7, 10, 9] corr, p stats.spearmanr(ranker_A, ranker_B) print(f两位评论员排名一致性的斯皮尔曼系数: {corr:.3f})5.3 金融数据分析分析股票价格排名与交易量排名的关系import pandas as pd # 假设我们有10只股票的价格变化和交易量变化数据 stocks pd.DataFrame({ price_change: [0.05, -0.02, 0.10, -0.03, 0.15, 0.01, -0.05, 0.08, 0.02, -0.01], volume_change: [0.20, -0.10, 0.15, -0.05, 0.30, 0.05, -0.15, 0.25, 0.08, -0.03] }) corr, p stats.spearmanr(stocks[price_change], stocks[volume_change]) print(f价格变化与交易量变化的秩相关性: {corr:.3f})6. 高级应用与注意事项6.1 处理缺失值spearmanr默认不支持缺失值需要先处理x [1, 2, 3, 4, np.nan, 6] y [2, 3, np.nan, 5, 6, 7] # 方法1删除含有缺失值的观测 mask ~(np.isnan(x) | np.isnan(y)) clean_x np.array(x)[mask] clean_y np.array(y)[mask] corr, p stats.spearmanr(clean_x, clean_y) # 方法2用中位数填充根据情况选择合适的方法 from sklearn.impute import SimpleImputer imputer SimpleImputer(strategymedian) x_imputed imputer.fit_transform(np.array(x).reshape(-1, 1)).flatten() y_imputed imputer.fit_transform(np.array(y).reshape(-1, 1)).flatten() corr, p stats.spearmanr(x_imputed, y_imputed)6.2 大样本与小样本检验小样本(n30)spearmanr会自动使用精确分布计算P值大样本(n≥30)使用正态近似计算P值6.3 多重比较问题当进行多个斯皮尔曼检验时可能需要考虑多重比较校正from statsmodels.stats.multitest import multipletests # 假设我们进行了5个独立的斯皮尔曼检验 p_values [0.01, 0.04, 0.03, 0.20, 0.005] # 使用Benjamini-Hochberg方法校正 rejected, adj_p_values, _, _ multipletests(p_values, methodfdr_bh) print(校正后的P值:, adj_p_values)6.4 可视化斯皮尔曼关系为了更好地理解数据关系可以绘制秩散点图def plot_rank_scatter(x, y): from scipy.stats import rankdata rank_x rankdata(x) rank_y rankdata(y) plt.scatter(rank_x, rank_y) plt.xlabel(Variable X Rank) plt.ylabel(Variable Y Rank) # 添加相关系数标注 corr stats.spearmanr(x, y)[0] plt.title(fRank Scatter Plot (ρ {corr:.2f})) # 添加趋势线 z np.polyfit(rank_x, rank_y, 1) p np.poly1d(z) plt.plot(rank_x, p(rank_x), r--) plt.show() # 使用前面的数据 plot_rank_scatter(x, y)7. 性能优化与替代方案对于非常大的数据集scipy.stats.spearmanr可能会比较慢。这时可以考虑7.1 使用Pandas的corr方法import pandas as pd df pd.DataFrame({x: x, y: y}) spearman_corr df.corr(methodspearman) print(spearman_corr)7.2 自定义快速实现对于超大数据集可以优化计算过程def fast_spearman(x, y): import numpy as np # 转换为numpy数组 x np.asarray(x) y np.asarray(y) # 计算秩 rank_x np.argsort(np.argsort(x)) rank_y np.argsort(np.argsort(y)) # 计算皮尔逊相关系数 cov np.cov(rank_x, rank_y)[0, 1] std_x np.std(rank_x) std_y np.std(rank_y) return cov / (std_x * std_y) # 测试 print(f自定义实现: {fast_spearman(x, y):.3f}) print(fscipy实现: {stats.spearmanr(x, y)[0]:.3f})7.3 其他相关方法在某些情况下这些替代方法可能更适合Kendalls Tau另一种秩相关系数对异常值更稳健Distance Correlation能够检测非线性依赖关系Mutual Information适用于更复杂的关系模式