相関データシミュレーションにコレスキー分解または代替を使用する方法


19

コレスキー分解を使用して、相関行列が与えられた相関ランダム変数をシミュレートします。事は、結果は与えられた相関構造を決して再現しないということです。以下に、状況を説明するためのPythonの小さな例を示します。

import numpy as np    

n_obs = 10000
means = [1, 2, 3]
sds = [1, 2, 3] # standard deviations 

# generating random independent variables 
observations = np.vstack([np.random.normal(loc=mean, scale=sd, size=n_obs)
                   for mean, sd in zip(means, sds)])  # observations, a row per variable

cor_matrix = np.array([[1.0, 0.6, 0.9],
                       [0.6, 1.0, 0.5],
                       [0.9, 0.5, 1.0]])

L = np.linalg.cholesky(cor_matrix)

print(np.corrcoef(L.dot(observations))) 

これは印刷します:

[[ 1.          0.34450587  0.57515737]
 [ 0.34450587  1.          0.1488504 ]
 [ 0.57515737  0.1488504   1.        ]]

ご覧のとおり、事後推定相関行列は前のものとは大きく異なります。私のコードにバグがありますか、それともコレスキー分解を使用する代わりの方法がありますか?

編集

この混乱を許してください。以前に研究した資料の誤解が原因で、コードおよび/またはコレスキー分解が適用された方法にエラーがあるとは思わなかった。実際、方法自体が正確であることを意図したものではないと確信しており、この質問を投稿させた状況までそれで大丈夫でした。私の誤解を指摘してくれてありがとう。@Silverfishが提案した実際の状況をよりよく反映するようにタイトルを編集しました。


1
Choleskyは問題なく動作し、これは実際に「私のコードのバグを見つけることができますか」というタイプの質問です。質問のタイトルと内容は、元々書かれているように、基本的には「コレスキーが機能しない、代替案は何ですか」ですか?これは、このサイトを検索するユーザーにとって非常に紛らわしいでしょう。これを反映してこの質問を編集する必要がありますか?(欠点は、javlacalleの回答の関連性が低くなることです。問題テキストは、検索者が実際にページ上で見つけるものを反映することです。)
Silverfish

@Antoni Parelladaはい、MATLABコードを(a)Python numpyに変換する正しい方法に変換したと思います。np.linalg.choleskyが下三角になるように調整し、MATLABのcholが上三角になるようにします。OPの誤ったコードをMATLABの同等のコードに既に変換し、誤った結果を複製していました。
マークL.ストーン

回答:


11

コレスキー分解に基づくアプローチは機能するはずです。ここ で説明し、この回答とほぼ同時に投稿されたマークL.ストーンの回答に示されています。

NμΣ

Y=Qバツ+μQ=Λ1/2Φ

YバツΦΣΛΣΦ

の例R(質問で使用したのと同じソフトウェアを使用していないのでごめんなさい):

n <- 10000
corM <- rbind(c(1.0, 0.6, 0.9), c(0.6, 1.0, 0.5), c(0.9, 0.5, 1.0))
set.seed(123)
SigmaEV <- eigen(corM)
eps <- rnorm(n * ncol(SigmaEV$vectors))
Meps <- matrix(eps, ncol = n, byrow = TRUE)    
Meps <- SigmaEV$vectors %*% diag(sqrt(SigmaEV$values)) %*% Meps
Meps <- t(Meps)
# target correlation matrix
corM
#      [,1] [,2] [,3]
# [1,]  1.0  0.6  0.9
# [2,]  0.6  1.0  0.5
# [3,]  0.9  0.5  1.0
# correlation matrix for simulated data
cor(Meps)
#           [,1]      [,2]      [,3]
# [1,] 1.0000000 0.6002078 0.8994329
# [2,] 0.6002078 1.0000000 0.5006346
# [3,] 0.8994329 0.5006346 1.0000000

この投稿この投稿にも興味があるかもしれません 。


再現された相関行列を正確にするには、データ生成手順に適用する前に、ランダム生成器からランダムデータのスプリアス相関を削除する必要があります。たとえば、ランダムデータの相関をepsでチェックして、最初にその偽の相関を確認します。
ゴットフリードヘルムズ

17

コードよりも単語や代数を使って何をしたかを説明した場合(または少なくとも擬似コードを使用して記述した場合)、エラーがはるかに早く見つかるでしょう。

あなたはこれと同等のことをしているように見えます(転置される可能性はありますが):

  1. 生成しますn×kZ

  2. σμ

  3. Y=Lバツ

L

あなたがすべきことはこれです:

  1. 生成しますn×kZ

  2. バツ=LZ

  3. σμ

このアルゴリズムについては、サイトで多くの説明があります。例えば

相関乱数を生成する方法(与えられた平均、分散、相関度)

コレスキー法を使用して、特定の平均値を持つ相関ランダム変数を生成できますか?

これは、目的の共分散行列に関して直接説明し、目的のサンプル共分散を取得するためのアルゴリズムも提供します。

特定のサンプル共分散行列を使用してデータを生成する


11

コレスキー分解には何の問題もありません。コードにエラーがあります。以下の編集を参照してください。

以下に、MATLABコードと結果を示します。最初にn_obs = 10000を使用し、次にn_obs = 1e8を使用します。簡単にするために、結果に影響を与えないため、手段を気にしません。つまり、ゼロにします。MATLABのcholは、行列Mの上三角コレスキー因子Rを生成し、R '* R = Mとなることに注意してください。numpy.linalg.choleskyは下三角コレスキー因子を生成するため、コードに対する調整が必要です。しかし、その点であなたのコードは問題ないと思います。

   >> correlation_matrix = [1.0, 0.6, 0.9; 0.6, 1.0, 0.5;0.9, 0.5, 1.0];
   >> SD = diag([1 2 3]);
   >> covariance_matrix = SD*correlation_matrix*SD
   covariance_matrix =
      1.000000000000000   1.200000000000000   2.700000000000000
      1.200000000000000   4.000000000000000   3.000000000000000
      2.700000000000000   3.000000000000000   9.000000000000000
   >> n_obs = 10000;
   >> Random_sample = randn(n_obs,3)*chol(covariance_matrix);
   >> disp(corr(Random_sample))
      1.000000000000000   0.599105015695768   0.898395949647890
      0.599105015695768   1.000000000000000   0.495147514173305
      0.898395949647890   0.495147514173305   1.000000000000000
   >> n_obs = 1e8;
   >> Random_sample = randn(n_obs,3)*chol(covariance_matrix);
   >> disp(corr(Random_sample))
      1.000000000000000   0.600101477583914   0.899986072541418
      0.600101477583914   1.000000000000000   0.500112824962378
      0.899986072541418   0.500112824962378   1.000000000000000

編集:あなたの間違いを見つけました。標準偏差を誤って適用しました。これはあなたがしたことと同等であり、間違っています。

   >> n_obs = 10000;
   >> Random_sample = randn(n_obs,3)*SD*chol(correlation_matrix);
   >> disp(corr(Random_sample))
      1.000000000000000   0.336292731308138   0.562331469857830
      0.336292731308138   1.000000000000000   0.131270077244625
      0.562331469857830   0.131270077244625   1.000000000000000
   >> n_obs=1e8;
   >> Random_sample = randn(n_obs,3)*SD*chol(correlation_matrix);
   >> disp(corr(Random_sample))
      1.000000000000000   0.351254525742470   0.568291702131030
      0.351254525742470   1.000000000000000   0.140443281045496
      0.568291702131030   0.140443281045496   1.000000000000000

6

CVはコードに関するものではありませんが、これがすべての良い答え、特に@Mark L. Stoneの貢献の後にどのように見えるか興味がありました。質問に対する実際の回答は、彼の投稿で提供されています(疑わしい場合は、彼の投稿を信用してください)。今後、この投稿の取得を容易にするために、この追加情報をここに移動します。他の優れた回答を軽視することなく、マークの回答の後、OPの投稿を修正することで問題を解決します。

ソース

パイソンで:

import numpy as np

no_obs = 1000             # Number of observations per column
means = [1, 2, 3]         # Mean values of each column
no_cols = 3               # Number of columns

sds = [1, 2, 3]           # SD of each column
sd = np.diag(sds)         # SD in a diagonal matrix for later operations

observations = np.random.normal(0, 1, (no_cols, no_obs)) # Rd draws N(0,1) in [3 x 1,000]

cor_matrix = np.array([[1.0, 0.6, 0.9],
                       [0.6, 1.0, 0.5],
                       [0.9, 0.5, 1.0]])          # The correlation matrix [3 x 3]

cov_matrix = np.dot(sd, np.dot(cor_matrix, sd))   # The covariance matrix

Chol = np.linalg.cholesky(cov_matrix)             # Cholesky decomposition

array([[ 1.        ,  0.        ,  0.        ],
       [ 1.2       ,  1.6       ,  0.        ],
       [ 2.7       , -0.15      ,  1.29903811]])

sam_eq_mean = Chol .dot(observations)             # Generating random MVN (0, cov_matrix)

s = sam_eq_mean.transpose() + means               # Adding the means column wise
samples = s.transpose()                           # Transposing back

print(np.corrcoef(samples))                       # Checking correlation consistency.

[[ 1.          0.59167434  0.90182308]
 [ 0.59167434  1.          0.49279316]
 [ 0.90182308  0.49279316  1.        ]]

IN [R]:

no_obs = 1000             # Number of observations per column
means = 1:3               # Mean values of each column
no_cols = 3               # Number of columns

sds = 1:3                 # SD of each column
sd = diag(sds)         # SD in a diagonal matrix for later operations

observations = matrix(rnorm(no_cols * no_obs), nrow = no_cols) # Rd draws N(0,1)

cor_matrix = matrix(c(1.0, 0.6, 0.9,
                      0.6, 1.0, 0.5,
                      0.9, 0.5, 1.0), byrow = T, nrow = 3)     # cor matrix [3 x 3]

cov_matrix = sd %*% cor_matrix %*% sd                          # The covariance matrix

Chol = chol(cov_matrix)                                        # Cholesky decomposition

     [,1] [,2]      [,3]
[1,]    1  1.2  2.700000
[2,]    0  1.6 -0.150000
[3,]    0  0.0  1.299038

sam_eq_mean = t(observations) %*% Chol          # Generating random MVN (0, cov_matrix)

samples = t(sam_eq_mean) + means

cor(t(samples))

          [,1]      [,2]      [,3]
[1,] 1.0000000 0.6071067 0.8857339
[2,] 0.6071067 1.0000000 0.4655579
[3,] 0.8857339 0.4655579 1.0000000

colMeans(t(samples))
[1] 1.035056 2.099352 3.065797
apply(t(samples), 2, sd)
[1] 0.9543873 1.9788250 2.8903964

1

他の人がすでに示したように、コレスキーは機能します。ここに、非常に短く、擬似コードに非常に近いコードの一部:MatMateのコードピース:

Co = {{1.0, 0.6, 0.9},  _
      {0.6, 1.0, 0.5},  _
      {0.9, 0.5, 1.0}}           // make correlation matrix


chol = cholesky(co)              // do cholesky-decomposition           
data = chol * unkorrzl(randomn(3,100,0,1))  
                                 // dot-multiply cholesky with random-
                                 // vectors with mean=0, sdev=1  
                                 //(refined by a "decorrelation" 
                                 //to remove spurious/random correlations)   


chk = data *' /100               // check the correlation of the data
list chk

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