アセットマネージャーのためのファインナンス機械学習:クラスタリング 実験 サンプル相関行列の作成
ブロック内の相関が高く、ブロック間の相関が低くなるように事前設定したブロック数$${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
![](https://assets.st-note.com/img/1691956431018-tSYFYZWiRo.png?width=1200)
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