アセットマネージャーのためのファインナンス機械学習:クラスタリング 実験 サンプル相関行列の作成

 ブロック内の相関が高く、ブロック間の相関が低くなるように事前設定したブロック数$${K}$$、各ブロックのサイズが$${M}$$以上の全体数Nの相関行列$${N\times N}$$をシャッフルする。このシャッフルされた相関行列から、ONCを用いて、ブロックが再現できることを検証する。
 
 引数$${(N,M,K}$$からサンプルの相関行列を作る。
 ブロックサイズがM以上のK個のブロックを作る。
 ブロック$${B_k, k=1,\dots,K}$$のそれぞれのサイズが$${x_k, x_k \gt M, \Sigma x_k=N \gt MK}$$とする。

 各ブロックに対し、標準ガウス分布に従う長さ$${T}$$の時系列を形成し、それを$${x_k}$$コピーして、行列$${T\times x_k}$$の行列を作り、各成分に$${\sigma \gt 0}$$の分散に従う乱数を加え乱数行列とし、この共分散行列を返す。ここで加えた分散の値がこのブロックの相関の強さとなる。この実装は、スニペット4.3で行われている。

from sklearn.utils import check_random_state

def getCovSub(nObs,nCols,sigma, random_state=None):
    rng=check_random_state(random_state)
    if nCols==1:
        return np.ones((1,1))
    ar0=rng.normal(size=(nObs,1))
    ar0=np.repeat(ar0,nCols,axis=1)
    ar0+=rng.normal(scale=sigma,size=ar0.shape) 
    ar0=np.cov(ar0,rowvar=False
    return ar0
nCols = 6
nObs = 8
sigma = 1.

tGetCovSub = getCovSub(nObs, nCols, sigma, random_state=None)
tGetCovSub.shape
getCovSub

GetCovSubのサイズを決めるのは、N個の要素をそれぞれサイズがM以上の重ならないグループにランダム分割する作業が必要である。
 この分割は、$${N'=N-K(M-1)}$$個をサイズが$${1}$$以上のK個のグループに分けることに等しい。整数の集合$${1,\dots,N'-1}$$から$${K-1}$$個の異なる項目を選び、そこに$${N'}$$を加えてサイズを$${K}$$にして、この数列$${B}$$とする。
数列$${B=\{i_1, i_2, \dots i_K\}, 1\le i_1 \le i_2 \dots \le i_K=N'}$$を使えば、分割$${C=\{C_1, \dots C_K\}}$$
$${C_1=0, 1, \dots i_1-1: i_1}$$
$${C_2=i_1, i_1+1, \dots i_2-1: i_2-i_1}$$
$${ \cdots }$$
$${C_K=i_{K-1}, i_{K-1}+1, \dots i_K-1: i_K-i_{K-1}}$$
となり、$${C_j}$$分割の要素数は$${i_j-i_{j-1}}$$であり、すべての$${i_j}$$数は異なることから、少なくとも一つの要素を持っている。
この分割に最小サイズ数$${M-1}$$を加えて一般化し、上記のgetCovSubに渡して、各ブロックサイズの時系列を作成し、全ブロックを結合した共分散行列を作成する。
 この実装は、getRndBlockCovで与えられている。

from scipy.linalg import block_diag
def getRndBlockCov(nCols,nBlocks,minBlockSize=1,sigma=1.,random_state=None):
    rng=check_random_state(random_state)
    parts=rng.choice(range(1,nCols-(minBlockSize-1)*nBlocks),
                    nBlocks-1,replace=False)
    parts.sort()
    parts=np.append(parts,nCols-(minBlockSize-1)*nBlocks)
    parts=np.append(parts[0],np.diff(parts))-1+minBlockSize
    cov=None
    for nCols_ in parts:
        cov_=getCovSub(int(max(nCols_*(nCols_+1)/2.,100)),nCols_,sigma,random_state=rng)
        if cov is None:
            cov=cov_.copy()
        else:
            cov=block_diag(cov,cov_)
    return cov

 ここで得られた全ブロックは各ブロック内では強い相関をもち、ブロック間の相関は低く抑えられている。これにさらに全体で一様な分散のランダムノイズをかけ、ONCの実用性を試す相関行列を作成する。

import MarPat as MP
def randomBlockCorr(nCols,nBlocks, random_state=None,minBlockSize=1):
    rng=check_random_state(random_state)
    cov0=getRndBlockCov(nCols,nBlocks,
                      minBlockSize=minBlockSize,sigma=.5,random_state=rng)
    cov1=getRndBlockCov(nCols,1,minBlockSize=minBlockSize,sigma=1.,random_state=rng)
    cov0+=cov1 # adding Noise
    corr0=MP.cov2corr(cov0)
    corr0=pd.DataFrame(corr0)
    return corr0

この記事が気に入ったらサポートをしてみませんか?