強い相関が存在する大きなフルランクランダム相関行列を生成する方法


25

適度に強い相関が存在するように、n × nサイズのランダム相関行列を生成したいと思います。Cn×n

  • サイズの正方実対称行列、たとえばn = 100 ;n×nn=100
  • 正定、つまり、すべての固有値が実数で正数の場合。
  • フルランク;
  • すべての対角要素が等しい。1
  • 非対角要素がなければならない合理的に均一に分布する。正確な分布は重要ではありませんが、適度に大きな値(たとえば、絶対値が0.5以上)をある程度適度に大きく(たとえば10 )したいと思います。基本的に、すべての非対角要素≈0Cがほぼ対角線上にないことを確認したいと思います(1,1)10%0.5C0

簡単な方法はありますか?

目的は、このようなランダム行列を使用して、相関(または共分散)行列を処理するアルゴリズムのベンチマークを行うことです。


動作しないメソッド

私が知っているランダム相関行列を生成するいくつかの方法を以下に示しますが、ここではうまくいきません。

  1. s × nサイズランダムなを生成し、中心化し、標準化して、相関行列C = 1を形成します。Xs×ns>nの場合、これにより、通常、すべての非対角相関が0付近になります。もしS«nは、いくつかの相関が強くなりますが、Cはフルランクではありません。C=1s1XXs>n0snC

  2. 次のいずれかの方法で、ランダムな正定行列を生成します。B

    • ランダム平方生成対称正定作るB = A AをAB=AA

    • ランダム平方発生対称にし、E = A + Aを、及び実行固有分解によって、それが正定値作るE = U S Uゼロにすべての負の固有値を設定する:B = UAE=A+AE=USU。注意:これにより、ランクが不足したマトリックスが生成されます。B=Umax{S,0}U

    • ランダムな直交を生成します(たとえば、ランダムな正方形Aを生成し、そのQR分解を行うか、グラムシュミットプロセスを介して)およびすべての正の要素を持つランダムな対角D。フォームB = Q D QQADB=QDQ

    得られた行列容易対角線上のすべてのものを有するように正規化することができる:C = D - 1 / 2 B D - 1 / 2Dは = dをI GBC=D1/2BD1/2同じ対角を有する対角行列であり、 BBを生成する上記の3つの方法はすべて、 Cの非対角要素が 0に近い結果になりますD=diagBBBC0


更新:古いスレッド

私の質問を投稿した後、私は過去に2つのほとんど重複を見つけました。

残念ながら、これらのスレッドには満足のいく答えが含まれていませんでした(今まで:)


1
QRまたはGram-Schmidtプロセスによってランダムな直交行列を作成できます。それは「PCAの固有ベクトル」です。列にスケールを追加します(「ローディング」に変わります)。これらの負荷から共分散行列を取得します。そのような何か...
ttnphns

1
ええと、.. nXk完全にランダムではなく、希望するロード行列W を作成することを想像してください(それは、WW'+diag(noise)求めるcov行列を定義します。唯一のタスクは、列正規化されたW(すなわち、k 「固有ベクトル」)直交する。相関変数(ここでは変数は固有ベクトルです)を相関
解除

1
ρ

3
あなたはLKJ分布に見たいと思うかもしれません
shadowtalker

2
@ttnphns:私はついにあなたがずっと正しかったことを理解したと思います:あなたが提案したのはゴールに到達する最も簡単な方法です。上記の内容を本質的に実装した更新プログラムを回答に追加しました。
アメーバは、モニカを復活させる14

回答:


14

他の答えは、さまざまな方法で私の問題を解決するための素晴らしいトリックを思いつきました。しかし、概念的に非常に明確で調整しやすいという大きな利点があると思う原則的なアプローチを見つけました。

このスレッドでは:ランダムな正半有限相関行列を効率的に生成する方法は?-ランダム相関行列を生成する2つの効率的なアルゴリズムのコードを説明および提供しました。どちらもLewandowski、Kurowicka、Joe(2009)の論文に由来し、@ ssdecontrolは上記のコメントで言及しました(どうもありがとう!)。

多くの図、説明、MATLABコードについては、私の回答をご覧ください。いわゆる「つる」法により、偏相関の分布を持つランダム相関行列を生成することができ、大きな非対角値を持つ相関行列を生成するために使用できます。そのスレッドの図の例を次に示します。

つるの方法

±1

betaparam50,20,10,5,2,1d100

function S = vineBeta(d, betaparam)
    P = zeros(d);           %// storing partial correlations
    S = eye(d);

    for k = 1:d-1
        for i = k+1:d
            P(k,i) = betarnd(betaparam,betaparam); %// sampling from beta
            P(k,i) = (P(k,i)-0.5)*2;     %// linearly shifting to [-1, 1]
            p = P(k,i);
            for l = (k-1):-1:1 %// converting partial correlation to raw correlation
                p = p * sqrt((1-P(l,i)^2)*(1-P(l,k)^2)) + P(l,i)*P(l,k);
            end
            S(k,i) = p;
            S(i,k) = p;
        end
    end

    %// permuting the variables to make the distribution permutation-invariant
    permutation = randperm(d);
    S = S(permutation, permutation);
end

更新:固有値

@psarkaは、これらの行列の固有値について尋ねます。下の図に、上記と同じ6つの相関行列の固有値スペクトルをプロットします。徐々に減少することに注意してください。対照的に、@ psarkaによって提案された方法は、一般に1つの大きな固有値を持つ相関行列になりますが、残りはかなり均一です。

上記の行列の固有値


更新。本当に簡単な方法:いくつかの要因

k<nWk×nWWDB=WW+Dk=100,50,20,10,5,1

ランダム因子からのランダム相関行列

k

これらの行列の固有スペクトル

コードは次のとおりです。

d = 100;    %// number of dimensions
k = 5;      %// number of factors

W = randn(d,k);
S = W*W' + diag(rand(1,d));
S = diag(1./sqrt(diag(S))) * S * diag(1./sqrt(diag(S)));

+1。ただし、ここでは、「ファクターメソッド」に関する最後のセクションを思い出してください。厳密に正しいアプローチでは、の列Wが直交(つまり、それらの間の余弦が0)であると呼びます。Wもちろん、ランダムに生成するだけでは提供されません。彼らは直交していない場合-すなわち要因は、(その後、呼び出し斜めあるWとしてW_) -要因は定理ではありませんWW'が、W_CW_'C要因間の「相関関係」(余弦)であること。今、C=Q'QとのQ回転の非直交回転行列であるW_=inv(Q)'W(などW=W_Q')。いくつかを生成しますQ-列ss = 1および行列ss =行列のサイズの行列。
ttnphns 14年

... typo:ないW_=inv(Q)'W、もちろんW_= W inv(Q)'
ttnphns 14年

WWW+DW

1
これをRに翻訳:W = replicate(k, rnorm(d)); S = W%*%t(W) + diag(rnorm(d),nrow=d); S = diag(1/sqrt(diag(S)))%*%S%*%diag(1/sqrt(diag(S)))
スコットウォーランド

1
@Mihai、良い点とあなたの提案はおそらく最も簡単です。あなたも行うことができますS <- matrix(nearPD(S, corr = TRUE, keepDiag = TRUE)$mat@x,ncol(S),ncol(S))
スコットWorlandの

7

a

import numpy as np
from random import choice
import matplotlib.pyplot as plt

n = 100
a = 2

A = np.matrix([np.random.randn(n) + np.random.randn(1)*a for i in range(n)])
A = A*np.transpose(A)
D_half = np.diag(np.diag(A)**(-0.5))
C = D_half*A*D_half

vals = list(np.array(C.ravel())[0])
plt.hist(vals, range=(-1,1))
plt.show()
plt.imshow(C, interpolation=None)
plt.show()

やや均一な分布 imshowの結果


crsk[a,a]X

はい、あなたは完全に正しいです!(ああ、それは本当にばかげていた:D)。ランダム部分をrandn(1)* aに変更しましたが、今ではずっと良くなっています。
プサルカ14年

k

an

この方法の欠点の1つは、結果の相関行列に1つの大きな固有値がありますが、残りの固有値はほぼ均一であることです。したがって、この手順では「一般的な」相関行列は得られません...質問で指定したわけではありません。しかし、@ ssdecontrolは上記のコメントで、明らかにすべての相関行列からサンプリングする方法があると述べました。これは興味深いように見えますが、はるかに複雑です。
アメーバは、モニカの復活を

6

うーん、MatMate言語で例を実行した後、既にpython-answerがあることがわかりました。これは、pythonが広く使用されているために望ましいかもしれません。しかし、まだ質問があったので、Matmate-matrix-languageを使用した私のアプローチを示します。おそらく、それはより自己コメント的です。

方法1
(MatMateを使用):

v=12         // 12 variables
f=3          // subset-correlation based on 3 common factors
vg = v / f   // variables per subsets

 // generate hidden factor-matrix
             // randomu(rows,cols ,lowbound, ubound) gives uniform random matrix 
             //    without explicite bounds the default is: randomu(rows,cols,0,100)
L = {   randomu(vg,f)     || randomu(vg,f)/100  || randomu(vg,f)/100 , _
        randomu(vg,f)/100 || randomu(vg,f)      || randomu(vg,f)/100 , _
        randomu(vg,f)/100 || randomu(vg,f)/100  || randomu(vg,f)     }

 // make sure there is itemspecific variance
 // by appending a diagonal-matrix with random positive entries
L = L || mkdiag(randomu(v,1,10,20)) 
  // make covariance and correlation matrix
cov = L *'   // L multiplied  with its transpose
cor = covtocorr(cov)
                   set ccdezweite=3 ccfeldweite=8
                   list cor
cor = 
   1.000,   0.321,   0.919,   0.489,   0.025,   0.019,   0.019,   0.030,   0.025,   0.017,   0.014,   0.014
   0.321,   1.000,   0.540,   0.923,   0.016,   0.015,   0.012,   0.030,   0.033,   0.016,   0.012,   0.015
   0.919,   0.540,   1.000,   0.679,   0.018,   0.014,   0.012,   0.029,   0.028,   0.014,   0.012,   0.012
   0.489,   0.923,   0.679,   1.000,   0.025,   0.022,   0.020,   0.040,   0.031,   0.014,   0.011,   0.014
   0.025,   0.016,   0.018,   0.025,   1.000,   0.815,   0.909,   0.758,   0.038,   0.012,   0.018,   0.014
   0.019,   0.015,   0.014,   0.022,   0.815,   1.000,   0.943,   0.884,   0.035,   0.012,   0.014,   0.012
   0.019,   0.012,   0.012,   0.020,   0.909,   0.943,   1.000,   0.831,   0.036,   0.013,   0.015,   0.010
   0.030,   0.030,   0.029,   0.040,   0.758,   0.884,   0.831,   1.000,   0.041,   0.017,   0.022,   0.020
   0.025,   0.033,   0.028,   0.031,   0.038,   0.035,   0.036,   0.041,   1.000,   0.831,   0.868,   0.780
   0.017,   0.016,   0.014,   0.014,   0.012,   0.012,   0.013,   0.017,   0.831,   1.000,   0.876,   0.848
   0.014,   0.012,   0.012,   0.011,   0.018,   0.014,   0.015,   0.022,   0.868,   0.876,   1.000,   0.904
   0.014,   0.015,   0.012,   0.014,   0.014,   0.012,   0.010,   0.020,   0.780,   0.848,   0.904,   1.000

ここでの問題は、内部に高い相関を持つサブマトリックスのブロックを定義し、その間にほとんど相関がなく、これはプログラムではなく、定数concatenation-expressionsによるものである可能性があります。たぶん、このアプローチはpythonでよりエレガントにモデル化できます。


方法2(a)
その後、完全に異なるアプローチがあります。100%のランダムな量で可能な残りの共分散をfactor-loadings-matrixに記入します。これはPari / GPで行われます。

{L = matrix(8,8);  \\ generate an empty factor-loadings-matrix
for(r=1,8, 
   rv=1.0;    \\ remaining variance for variable is 1.0
   for(c=1,8,
        pv=if(c<8,random(100)/100.0,1.0); \\ define randomly part of remaining variance
        cv= pv * rv;  \\ compute current partial variance
        rv = rv - cv;     \\ compute the now remaining variance
        sg = (-1)^(random(100) % 2) ;  \\ also introduce randomly +- signs
        L[r,c] = sg*sqrt(cv) ;  \\ compute factor loading as signed sqrt of cv
       )
     );}

cor = L * L~

生成された相関行列は

     1.000  -0.7111  -0.08648   -0.7806   0.8394  -0.7674   0.6812    0.2765
   -0.7111    1.000   0.06073    0.7485  -0.7550   0.8052  -0.8273   0.05863
  -0.08648  0.06073     1.000    0.5146  -0.1614   0.1459  -0.4760  -0.01800
   -0.7806   0.7485    0.5146     1.000  -0.8274   0.7644  -0.9373  -0.06388
    0.8394  -0.7550   -0.1614   -0.8274    1.000  -0.5823   0.8065   -0.1929
   -0.7674   0.8052    0.1459    0.7644  -0.5823    1.000  -0.7261   -0.4822
    0.6812  -0.8273   -0.4760   -0.9373   0.8065  -0.7261    1.000   -0.1526
    0.2765  0.05863  -0.01800  -0.06388  -0.1929  -0.4822  -0.1526     1.000

おそらく、これは、因子負荷行列の累積的な生成規則のために、支配的な主成分を持つ相関行列を生成します。また、分散の最後の部分を一意の要素にすることで、正の明確性を保証する方がよい場合があります。一般原則に焦点を当て続けるために、プログラムに残しました。

100x100の相関行列には、次の相関頻度がありました(1桁に丸められています)

    e    f            e: entry(rounded) f: frequency
  -----------------------------------------------------
  -1.000, 108.000
  -0.900, 460.000
  -0.800, 582.000
  -0.700, 604.000
  -0.600, 548.000
  -0.500, 540.000
  -0.400, 506.000
  -0.300, 482.000
  -0.200, 488.000
  -0.100, 464.000
   0.000, 434.000
   0.100, 486.000
   0.200, 454.000
   0.300, 468.000
   0.400, 462.000
   0.500, 618.000
   0.600, 556.000
   0.700, 586.000
   0.800, 536.000
   0.900, 420.000
   1.000, 198.000

[更新]。うーん、100x100マトリックスの条件は悪いです。Pari / GPは、精度が200桁であっても、polroots(charpoly())関数を使用して固有値を正しく決定できません。loadingsmatrix Lでpca-formにヤコビ回転を行い、ほとんどが非常に小さな固有値を見つけ、10を底とする対数で出力します(これにより、おおよそ小数点の位置がわかります)。左から右に読んでから、行ごとに読む:

log_10(eigenvalues):
   1.684,   1.444,   1.029,   0.818,   0.455,   0.241,   0.117,  -0.423,  -0.664,  -1.040
  -1.647,  -1.799,  -1.959,  -2.298,  -2.729,  -3.059,  -3.497,  -3.833,  -4.014,  -4.467
  -4.992,  -5.396,  -5.511,  -6.366,  -6.615,  -6.834,  -7.535,  -8.138,  -8.263,  -8.766
  -9.082,  -9.482,  -9.940, -10.167, -10.566, -11.110, -11.434, -11.788, -12.079, -12.722
 -13.122, -13.322, -13.444, -13.933, -14.390, -14.614, -15.070, -15.334, -15.904, -16.278
 -16.396, -16.708, -17.022, -17.746, -18.090, -18.358, -18.617, -18.903, -19.186, -19.476
 -19.661, -19.764, -20.342, -20.648, -20.805, -20.922, -21.394, -21.740, -21.991, -22.291
 -22.792, -23.184, -23.680, -24.100, -24.222, -24.631, -24.979, -25.161, -25.282, -26.211
 -27.181, -27.626, -27.861, -28.054, -28.266, -28.369, -29.074, -29.329, -29.539, -29.689
 -30.216, -30.784, -31.269, -31.760, -32.218, -32.446, -32.785, -33.003, -33.448, -34.318

[更新2]
方法2(b)
改善点は、アイテム固有の分散を非限界レベルに増やし、一般的な要因のかなり少ない数(たとえば、アイテム番号の整数平方根)に減らすことです。

{  dimr = 100;
   dimc = sqrtint(dimr);        \\ 10 common factors
   L = matrix(dimr,dimr+dimc);  \\ loadings matrix 
                                \\     with dimr itemspecific and 
                                \\          dimc common factors
   for(r=1,dim, 
         vr=1.0;                \\ complete variance per item 
         vu=0.05+random(100)/1000.0;   \\ random variance +0.05
                                       \\ for itemspecific variance
         L[r,r]=sqrt(vu);              \\ itemspecific factor loading  
         vr=vr-vu;
         for(c=1,dimc,
                cv=if(c<dimc,random(100)/100,1.0)*vr;
                vr=vr-cv;
                L[r,dimr+c]=(-1)^(random(100) % 2)*sqrt(cv)
             )
        );}

   cov=L*L~
   cp=charpoly(cov)   \\ does not work even with 200 digits precision
   pr=polroots(cp)    \\ spurious negative and complex eigenvalues...

結果の構造

相関の分布に関して:画像

同様に(PariGPによる厄介な非分解性も)ですが、loadingsmatrixのjacobi-rotationで見つかった固有値はより良い構造になりました。新しく計算された例では、

log_10(eigenvalues):
   1.677,   1.326,   1.063,   0.754,   0.415,   0.116,  -0.262,  -0.516,  -0.587,  -0.783
  -0.835,  -0.844,  -0.851,  -0.854,  -0.858,  -0.862,  -0.862,  -0.868,  -0.872,  -0.873
  -0.878,  -0.882,  -0.884,  -0.890,  -0.895,  -0.896,  -0.896,  -0.898,  -0.902,  -0.904
  -0.904,  -0.909,  -0.911,  -0.914,  -0.920,  -0.923,  -0.925,  -0.927,  -0.931,  -0.935
  -0.939,  -0.939,  -0.943,  -0.948,  -0.951,  -0.955,  -0.956,  -0.960,  -0.967,  -0.969
  -0.973,  -0.981,  -0.986,  -0.989,  -0.997,  -1.003,  -1.005,  -1.011,  -1.014,  -1.019
  -1.022,  -1.024,  -1.031,  -1.038,  -1.040,  -1.048,  -1.051,  -1.061,  -1.064,  -1.068
  -1.070,  -1.074,  -1.092,  -1.092,  -1.108,  -1.113,  -1.120,  -1.134,  -1.139,  -1.147
  -1.150,  -1.155,  -1.158,  -1.166,  -1.171,  -1.175,  -1.184,  -1.184,  -1.192,  -1.196
  -1.200,  -1.220,  -1.237,  -1.245,  -1.252,  -1.262,  -1.269,  -1.282,  -1.287,  -1.290

どうもありがとう!非常に興味深いが、私に消化するのに時間がかかります...
アメーバは回復モニカ言う

私はまだあなたの答えを注意深く調べる必要がありますが、その間にランダム相関行列のサンプリングに関する論文を読み、そこからの方法の1つを使用して正確に必要なことを行うことができます。私はここに答えを投稿しました、あなたは見てみることに興味があるかもしれません!別のスレッドで書いたより詳細な回答にリンクしています。
アメーバは、モニカを復活させる14

@amoeba:あなたのためにうまく機能しているものを見つけて幸せです!興味深い質問です。後で自分自身に戻って、あなたが取り組んだ論文に従って、MatMateの手順を改善/適応(およびサブルーチン化)するかもしれません。
ゴットフリードヘルムズ14年

2

ABλA+(1λ)Bλ

ABCλAA+λBB+λCCλ=1λ0


AB

ああ、しかし、そのようなアルゴリズムと、正定相関行列のポリトープを定義する「頂点」(つまり行列)の適切な多様性から、拒絶サンプリングを使用して固有値の分布、エントリの均一性、など、あなたが望むこと。しかし、良い基礎が何であるかは私には明らかではありません。さらに最近I.より抽象代数学を勉強した人のための質問のように聞こえる
アンドリュー・M

こんにちは、私はランダム相関行列のサンプリングに関する論文を読みました。そこからの方法の1つを使用して、必要なことを正確に行うことができます。私はここに答えを投稿しました、あなたは見てみることに興味があるかもしれません!別のスレッドで書いたより詳細な回答にリンクしています。
アメーバは、Reinstate Monica 14年

2

Rには、次のメソッドを実装するパッケージ(clusterGeneration)があります。

例:

> (cormat10 = clusterGeneration::rcorrmatrix(10, alphad = 1/100000000000000))
        [,1]   [,2]    [,3]     [,4]     [,5]   [,6]   [,7]    [,8]     [,9]   [,10]
 [1,]  1.000  0.344 -0.1406 -0.65786 -0.19411  0.246  0.688 -0.6146  0.36971 -0.1052
 [2,]  0.344  1.000 -0.4256 -0.35512  0.15973  0.192  0.340 -0.4907 -0.30539 -0.6104
 [3,] -0.141 -0.426  1.0000  0.01775 -0.61507 -0.485 -0.273  0.3492 -0.30284  0.1647
 [4,] -0.658 -0.355  0.0178  1.00000  0.00528 -0.335 -0.124  0.5256 -0.00583 -0.0737
 [5,] -0.194  0.160 -0.6151  0.00528  1.00000  0.273 -0.350 -0.0785  0.08285  0.0985
 [6,]  0.246  0.192 -0.4847 -0.33531  0.27342  1.000  0.278 -0.2220 -0.11010  0.0720
 [7,]  0.688  0.340 -0.2734 -0.12363 -0.34972  0.278  1.000 -0.6409  0.40314 -0.2800
 [8,] -0.615 -0.491  0.3492  0.52557 -0.07852 -0.222 -0.641  1.0000 -0.50796  0.1461
 [9,]  0.370 -0.305 -0.3028 -0.00583  0.08285 -0.110  0.403 -0.5080  1.00000  0.3219
[10,] -0.105 -0.610  0.1647 -0.07373  0.09847  0.072 -0.280  0.1461  0.32185  1.0000
> cormat10[lower.tri(cormat10)] %>% psych::describe()
   vars  n  mean   sd median trimmed mad   min  max range skew kurtosis   se
X1    1 45 -0.07 0.35  -0.08   -0.07 0.4 -0.66 0.69  1.35 0.03       -1 0.05

残念ながら、これで均一な分布に従う相関をシミュレートすることは不可能と思われます。alphadを非常に小さな値に設定すると相関が強くなるようですが、でも1/100000000000000、相関の範囲は約1.40までしか上がりません。

それにもかかわらず、私はこれが誰かの役に立つことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.