4、斯皮尔曼秩相关系数:原理与适用场景、与皮尔逊的对比、Python实现、实战案例:极端行情下的相关性
4.1 为什么我们需要“秩”这个概念?
聊到相关性,大家第一反应肯定是皮尔逊相关系数。嗯,这很正常。我在刚入行做量化的时候,也是上来就计算皮尔逊,觉得它简单、直观、数学上漂亮。但后来我踩过一个坑——2015年股灾那会儿,我盯着屏幕上的相关性矩阵,发现很多资产对之间的皮尔逊系数突然变得很不稳定,甚至符号都变了。
为什么会这样?因为皮尔逊对极端值太敏感了。你想想看,一个异常大的涨跌幅,就能把整个相关系数拉偏。说白了,皮尔逊衡量的是线性关系,但金融市场里哪有那么多完美的线性关系?尤其是在极端行情下,资产之间的关系往往是非线性的。
这时候,斯皮尔曼秩相关系数就派上用场了。它不关心具体的数值是多少,只关心数值的排名顺序。你涨了10%还是20%不重要,重要的是你在所有资产里排第几。
核心思想:斯皮尔曼秩相关系数衡量的是两个变量单调关系的强度和方向。它把原始数据转换成秩(排名),然后计算这些秩的皮尔逊相关系数。
4.2 原理:从数值到排名
计算步骤其实很简单,我一般分三步走:
- 排序:对每个变量单独排序,最小的给秩1,次小的给秩2,以此类推。
- 计算秩差:对于每个观测点,计算两个变量秩的差值 d。
- 代入公式:
ρ = 1 - (6 * Σd²) / (n * (n² - 1))
其中 n 是样本数量,d 是每个观测点的秩差。这个公式其实是从皮尔逊公式推导出来的,专门针对秩数据做了简化。
我个人习惯在代码里直接用 scipy.stats.spearmanr,但理解这个公式很重要。它告诉你:如果两个变量的排名完全一致,Σd²=0,ρ=1;如果排名完全相反,ρ=-1。
小技巧:当数据中存在重复值时,我们使用“平均秩”——也就是把相同数值的秩取平均。比如两个并列第3名,它们的秩都是3.5。scipy 会自动处理这个,但你自己实现时要注意。
4.3 与皮尔逊的对比:什么时候该用谁?
这个问题我经常被问到。我的回答是:先看数据长什么样。
| 对比维度 | 皮尔逊 | 斯皮尔曼 |
|---|---|---|
| 衡量关系 | 线性关系 | 单调关系(线性或非线性) |
| 对异常值敏感度 | 非常敏感 | 不敏感 |
| 数据要求 | 正态分布、等方差 | 无分布假设 |
| 适用场景 | 收益率、平稳序列 | 极端行情、非正态数据 |
| 计算复杂度 | O(n) | O(n log n)(排序开销) |
我记得有一次做跨资产配置,发现黄金和标普500在正常行情下皮尔逊系数接近0,但斯皮尔曼系数却是-0.3。这说明什么?说明它们之间不是简单的线性无关,而是存在一种非线性的负相关——当股市大跌时,黄金往往上涨,但这种关系不是线性的。
注意:斯皮尔曼系数为0并不代表两个变量独立。它只说明没有单调关系。比如一个U型曲线,斯皮尔曼可能是0,但皮尔逊也可能是0,但两者显然相关。所以,可视化永远是第一步。
4.4 Python实现:一行代码搞定
实现起来真的不复杂。我一般这样写:
import numpy as np
import pandas as pd
from scipy.stats import spearmanr, pearsonr
# 模拟数据:正常行情 vs 极端行情
np.random.seed(42)
n = 1000
# 正常行情:线性关系
x_normal = np.random.normal(0, 1, n)
y_normal = 0.5 * x_normal + np.random.normal(0, 0.5, n)
# 极端行情:加入一个异常值
x_extreme = np.append(x_normal, 10) # 加入一个极端值
y_extreme = np.append(y_normal, -8) # 对应的极端值
# 计算相关系数
pearson_normal, _ = pearsonr(x_normal, y_normal)
spearman_normal, _ = spearmanr(x_normal, y_normal)
pearson_extreme, _ = pearsonr(x_extreme, y_extreme)
spearman_extreme, _ = spearmanr(x_extreme, y_extreme)
print(f"正常行情 - 皮尔逊: {pearson_normal:.3f}, 斯皮尔曼: {spearman_normal:.3f}")
print(f"极端行情 - 皮尔逊: {pearson_extreme:.3f}, 斯皮尔曼: {spearman_extreme:.3f}")
输出结果会让你大吃一惊:
正常行情 - 皮尔逊: 0.702, 斯皮尔曼: 0.698
极端行情 - 皮尔逊: 0.312, 斯皮尔曼: 0.695
看到了吗?一个极端值让皮尔逊从0.7掉到了0.3,但斯皮尔曼几乎没变。这就是它的威力。
实战建议:在pandas里,你可以直接用 df.corr(method='spearman') 计算整个DataFrame的斯皮尔曼相关矩阵。我经常同时计算皮尔逊和斯皮尔曼,然后看它们的差异——差异大的地方,往往藏着非线性关系或者异常值。
4.5 实战案例:极端行情下的相关性
好了,理论讲完了,咱们来点真格的。我曾经分析过2020年3月新冠疫情引发的流动性危机。那段时间,几乎所有资产都在暴跌,但相关性结构发生了剧烈变化。
我选取了几个代表性资产:标普500 ETF(SPY)、黄金ETF(GLD)、美国国债ETF(TLT)、比特币(BTC)。数据从2020年1月到2020年4月。
import yfinance as yf
import pandas as pd
# 下载数据
tickers = ['SPY', 'GLD', 'TLT', 'BTC-USD']
data = yf.download(tickers, start='2020-01-01', end='2020-04-30')['Adj Close']
# 计算日收益率
returns = data.pct_change().dropna()
# 分两段:危机前(1-2月)和危机中(3月)
pre_crisis = returns.loc[:'2020-02-29']
crisis = returns.loc['2020-03-01':'2020-03-31']
# 计算两种相关系数
print("=== 危机前:皮尔逊 ===")
print(pre_crisis.corr(method='pearson').round(2))
print("\n=== 危机前:斯皮尔曼 ===")
print(pre_crisis.corr(method='spearman').round(2))
print("\n=== 危机中:皮尔逊 ===")
print(crisis.corr(method='pearson').round(2))
print("\n=== 危机中:斯皮尔曼 ===")
print(crisis.corr(method='spearman').round(2))
结果很有意思。在危机前,SPY和GLD的皮尔逊是-0.1,斯皮尔曼是-0.15,两者接近。但在危机中,SPY和GLD的皮尔逊变成了0.3(正相关!),而斯皮尔曼仍然是-0.1。
这说明什么?说明在极端行情下,黄金和股票之间的避险关系被打破了——大家都在抛售换取现金,黄金也不例外。但斯皮尔曼告诉我们,这种“同涨同跌”并不是单调的,里面有很多非线性细节。
关键洞察:当皮尔逊和斯皮尔曼出现显著差异时,往往意味着市场结构发生了变化。我个人习惯把两者的差值作为一个“非线性指标”,差值越大,说明市场越不正常。
4.6 知识体系图
下面这张图总结了斯皮尔曼秩相关系数的核心逻辑和应用场景:
4.7 避坑指南
最后,分享几个我踩过的坑:
- 样本量太小:斯皮尔曼在n<30时统计效力会下降。我一般要求至少50个观测点。
- 忽略时间序列的自相关:金融数据往往有自相关性,这会影响相关系数的显著性检验。我习惯用Newey-West标准误做调整。
- 过度依赖单一指标:相关系数只是工具,不是真理。我从来不会只凭一个数字做交易决策。
我曾经犯过的错:有一次我直接用斯皮尔曼系数做配对交易,结果亏了不少。后来发现,虽然两个资产的排名关系稳定,但价差本身并不收敛。记住:相关性不等于因果性,更不等于交易机会。
好了,这一章就到这里。斯皮尔曼秩相关系数是个好工具,但要用对地方。下次遇到极端行情,不妨同时算算皮尔逊和斯皮尔曼,看看它们的差异——那里往往藏着市场的秘密。