请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  数理科学 帖子:3353723 新帖:34

【QLS】斯皮尔曼秩相关系数

耶伦发表于:5 月 9 日 18:35回复(1)

斯皮尔曼秩相关系数

斯皮尔曼秩相关系数¶

  • By Evgenia "Jenny" Nitishinskaya and Delaney Granizo-Mackenzie with example algorithms by David Edwards
  • 原文载于https://www.quantopian.com/research/notebooks/Cloned%20from%20%22Quantopian%20Lecture%20Series%3A%20Updated%20Spearman%20Rank%20Correlation%20Notebook%22%206.ipynb?
  • 翻译,修改以及本地化 Kris

斯皮尔曼秩相关系数可以用于检测两组数据是否同向移动,即其中一组数据增加(减少)时,另一组数据是否跟着增加(减少)。这比线性关系更加广泛:例如,$y=e^x$是一个单调函数,但不是一个线性函数。因此,在计算时,我们不是直接使用原始数据,而是数据的秩。 特别是当数据单位不统一时(那么他们自然就不存在线性关系),这种方法非常有用。比如,一块正方形土地的价格和它的边长之间一般不成线性关系,因为按照常理,价格应该跟面积线性相关而不是边长。此外,在对回归模型参数的显著性进行检定时,通常需要对观测值的分布做一些假定,例如使用t分布就需要观测值服从正态分布,但有的数据集不一定满足我们所做的假设,从而无法使用,但是斯皮尔曼秩相关系数就不需要这些假定,仍然可以使用。

import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import math
import pandas as pd
# 对数据进行分级的一个例子
l = [10, 9, 5, 7, 5]
print '原始数据: ', l
print '秩: ', list(stats.rankdata(l, method='average'))
原始数据:  [10, 9, 5, 7, 5]
秩:  [5.0, 4.0, 1.5, 3.0, 1.5]

斯皮尔曼秩相关性¶

定义¶

用$r_S$表示斯皮尔曼秩相关系数 要计算两个数据集X,Y(每个里面包含n个数据)的等级,使用公式 $$r_S = 1 - \frac{6 \sum_{i=1}^n d_i^2}{n(n^2 - 1)}$$

$d_i$是第i对数据的等级之差,即$X_i - Y_i$.

这个结果总是在$-1$到$1$之间。结果为正代表变量间存在正相关,结果为负就表示负相关。结果为0代表两组变量之间没有任何额单调性关系,不过这并不是说它们之间没有任何的相关关系,比如,对于两组时间序列X和Y,Y是X滞后两期的变量,显然这两组变量是相关的,但是它们的$r_S$可以非常接近于0

## 示例
n = 100

def compare_correlation_and_spearman_rank(n, noise):
    X = np.random.poisson(size=n)
    Y = np.exp(X) + noise * np.random.normal(size=n)

    Xrank = stats.rankdata(X, method='average')
    # n-2是指倒数第二个元素 
    Yrank = stats.rankdata(Y, method='average')

    diffs = Xrank - Yrank # 这个地方Xrank和Yrank的顺序不重要,因为要对它们进行平方 
    r_s = 1 - 6*sum(diffs*diffs)/(n*(n**2 - 1))
    c_c = np.corrcoef(X, Y)[0,1]
    
    return r_s, c_c

experiments = 1000
spearman_dist = np.ndarray(experiments)
correlation_dist = np.ndarray(experiments)
for i in range(experiments):
    r_s, c_c = compare_correlation_and_spearman_rank(n, 1.0)
    spearman_dist[i] = r_s
    correlation_dist[i] = c_c
    
print '斯皮尔曼秩相关系数: ' + str(np.mean(spearman_dist))
# 与通常意义上的相关系数进行对比 
print '通常意义下的相关系数: ' + str(np.mean(correlation_dist))
斯皮尔曼秩相关系数: 0.877291422142
通常意义下的相关系数: 0.769284543469

试验¶

从泊松分布中取一组数据(不服从正态分布),然后令$Y = e^X + \epsilon$,$\epsilon$是另一个泊松分布的均值,再求出这两组数据的斯皮尔曼秩相关系数和它们的相关系数,这样重复进行很多次。 由于$e^X$会产生很多与其它的数据偏离很远的数据,因此我们用它来模拟数据集中的极端值。斯皮尔曼秩相关系数可以有效减弱极端值的影响,从而对变量间的相关性做出更好的衡量。而通常意义上的相关系数受极端值的影响比较大,经常会低估变量间的相关性。

我们来看一看X,Y间的相关性并把用斯皮尔曼的方法和一般的方法得出的结果进行比较

plt.hist(spearman_dist, bins=50, alpha=0.5)
plt.hist(correlation_dist, bins=50, alpha=0.5)
plt.legend(['Spearman Rank', 'Regular Correlation'])
plt.xlabel('Correlation Coefficient')
plt.ylabel('Frequency');

现在,我们再来看当在数据中加入一些干扰时,会出现什么情况。

n = 100
noises = np.linspace(0, 3, 30)
experiments = 100
spearman = np.ndarray(len(noises))
correlation = np.ndarray(len(noises))

for i in range(len(noises)):
    # Run many experiments for each noise setting
    rank_coef = 0.0
    corr_coef = 0.0
    noise = noises[i]
    for j in range(experiments):
        r_s, c_c = compare_correlation_and_spearman_rank(n, noise)
        rank_coef += r_s
        corr_coef += c_c
    spearman[i] = rank_coef/experiments
    correlation[i] = corr_coef/experiments
    
plt.scatter(noises, spearman, color='r')
plt.scatter(noises, correlation)
plt.legend(['Spearman', 'regular'])
plt.xlabel('amount of noise')
plt.ylabel('average correlational coefficient')
<matplotlib.text.Text at 0x7f1d7739a810>

可以看到,斯皮尔曼秩相关系数在不同程度的干扰下基本都能较好的衡量相关性。不过在干扰程度特别强的时候,它似乎会比通常意义上的相关系数表现的要差

存在延迟相关性的延迟¶

有时我们会碰到这样的情况,两组变量相互影响,但是会有一个延迟。让我们来看看加入延迟后会出现什么现象。

n = 100

X = np.random.rand(n)
Xrank = stats.rankdata(X, method='average')
# n-2 is the second to last element
Yrank = stats.rankdata([1,1] + list(X[:(n-2)]), method='average')

diffs = Xrank - Yrank # order doesn't matter since we'll be squaring these values
r_s = 1 - 6*sum(diffs*diffs)/(n*(n**2 - 1))
print r_s
-0.0991209120912

显然,这种相关性没有被捕捉到。因此,在存在延迟时,要同时使用斯皮尔曼秩相关系数和通常意义上的相关系数来避免遗漏,同时,还应该去掉延迟,用多组平行数据进行检测。

内置函数¶

我们也可以使用scipy.stats库里的spearmanr函数

# 生成两组随机数 
np.random.seed(161)
X = np.random.rand(10)
Y = np.random.rand(10)

r_s = stats.spearmanr(X, Y)
print '斯皮尔曼秩相关系数: ', r_s[0]
print 'p-value: ', r_s[1]
斯皮尔曼秩相关系数:  0.236363636364
p-value:  0.510885317515

现在我们有了定义的$r_S$,但是它的含义到底是什么呢?它取正值时,我们知道这表示变量间不是负相关。它的值不大,那么我们知道变量间的正相关性不是很强,但是一眼看上去我们很难判断这种相关关系是否显著。幸运的是, 斯皮尔曼同时也会计算相关系数的p-value和样本大小。我们可以看到此处的p-value大于0.05,因此,我们不能说X和Y是相关的。

实例:共同基金的费用比¶

现在我们知道了斯皮尔曼等级相关系数的原理,那么我们就来用一个具体的实例来演示一下。比如,可以检测一个共同基金的费用与它三年的夏普比是否相关。也就是说,一个共同基金花费越多,是否意味着它的风险越低或者收益率越高?这个问题就可以使用斯皮尔曼等级相关系数来检验,此处我们默认p-value为0.05。

数据源¶

感谢Matthew Madurski提供的数据。获取数据的步骤:

  • 1.从这个链接中下载这个csv文件:https://gist.github.com/dursk/82eee65b7d1056b469ab
  • 2.将这个文件上传到你的研究账户中
mutual_fund_data= pd.read_csv('mutual_fund_data.csv')
expense = mutual_fund_data['Annual Expense Ratio'].values
sharpe = mutual_fund_data['Three Year Sharpe Ratio'].values
 
plt.scatter(expense, sharpe)
plt.xlabel('Expense Ratio')
plt.ylabel('Sharpe Ratio')

r_S = stats.spearmanr(expense, sharpe)
print '斯皮尔曼秩相关系数: ', r_S[0]
print 'p-value: ', r_S[1]
斯皮尔曼秩相关系数:  -0.237573932355
p-value:  0.0167465097116

求得的p-value小于0.05,说明原假设是正确的。花费确实与夏普比相关,且从结果来看,它们之间是成负相关的。然而,这里有一个奇怪的地方,从图中可以看到,数据有聚集的现象。从上图来看,有些花费多的共同基金夏普比比较低,而大多数的共同基金的夏普比与花费没有明显的相关关系。要弄清楚这里具体是什么原因,还需要进一步的分析。

实际中的应用:检测股票收益率的相关性¶

以计算机/互联网行业的股票为例,假设这个行业中的股票上个月的平均收益率与这个月的平均收益率成正相关,现在我们来检测这个假设是否成立。

symbol_list =get_industry_stocks('I64')

# 获取上个月的收益率
start = '2015-08-01'
end = '2015-09-01'
historical_returns = get_price(symbol_list, fields='price', start_date=start, end_date=end).pct_change()[0:]

# 计算各只股票的平均收益率
scores = np.mean(historical_returns)
print 'average return of last month\n'
print scores
print '\n'

#获取这个月的收益率
start = '2015-09-01'
end = '2015-10-01'
walk_forward_returns = get_price(symbol_list, fields='price', start_date=start, end_date=end).pct_change()[0:]

#计算各只股票的平均收益率
walk_forward_returns = np.mean(walk_forward_returns)
print 'average return of this month\n'
print walk_forward_returns
print '\n'

plt.scatter(scores, walk_forward_returns)
plt.xlabel('average return of last month')
plt.ylabel('average return of this month')

r_s = stats.spearmanr(scores, walk_forward_returns)
print '相关系数: ' + str(r_s[0])
print 'p-value: ' + str(r_s[1])
average return of last month

                price
000503.XSHE -0.019558
002095.XSHE -0.005533
002174.XSHE  0.000000
002315.XSHE -0.007950
002354.XSHE  0.004093
002439.XSHE -0.011898
002467.XSHE -0.015432
300059.XSHE -0.012561
300104.XSHE -0.012610
300113.XSHE -0.024175
300431.XSHE -0.017393
300467.XSHE  0.000000
300226.XSHE -0.009058
300295.XSHE -0.009890
300383.XSHE  0.000000
300392.XSHE -0.014040
300418.XSHE -0.032626
600804.XSHG -0.007636
603000.XSHG -0.007722


average return of this month

                price
000503.XSHE  0.005466
002095.XSHE  0.000735
002174.XSHE  0.000000
002315.XSHE  0.006041
002354.XSHE  0.004739
002439.XSHE  0.016386
002467.XSHE  0.007491
300059.XSHE  0.005429
300104.XSHE  0.009936
300113.XSHE  0.016674
300431.XSHE  0.026902
300467.XSHE  0.000000
300226.XSHE  0.021097
300295.XSHE  0.012210
300383.XSHE  0.000000
300392.XSHE  0.022171
300418.XSHE  0.017337
600804.XSHG  0.009990
603000.XSHG -0.001316


相关系数: -0.68485915493
p-value: 0.00121542282477

p-value小于0.05,而相关系数为负。显然,假设是不成立的。也就是说采用斯皮尔曼等级相关系数检测的结果表明计算机/互联网行业的股票在2015年9月到10月的平均收益率与上一个与月的平均收益率是成负相关的。

全部回复

0/140

量化课程

    移动端课程