상관 된 데이터 시뮬레이션을 위해 Cholesky 분해 또는 대안을 사용하는 방법


19

Cholesky 분해를 사용하여 상관 행렬이 주어지면 상관 랜덤 변수를 시뮬레이션합니다. 문제는 주어진 상관 관계 구조를 결코 재현하지 못한다는 것입니다. 다음은 상황을 설명하기위한 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.        ]]

보시다시피, 사후 추정 상관 행렬은 이전과 크게 다릅니다. 내 코드에 버그가 있습니까, 아니면 Cholesky 분해 사용에 대한 대안이 있습니까?

편집하다

이 엉망으로 당신의 사면을 간청합니다. 필자는 이전에 연구 한 자료에 대한 약간의 오해로 인해 코드에 오류가 있거나 Cholesky 분해가 적용되는 방식에 있다고 생각하지 않았습니다. 사실 나는 그 방법 자체가 정확하지 않다는 것을 확신 했으며이 질문을 게시 한 상황이 될 때까지 그 방법에 대해 괜찮 았습니다. 내가 가진 오해를 지적 해 주셔서 감사합니다. @Silverfish가 제안한 실제 상황을 더 잘 반영하도록 제목을 편집했습니다.


1
Cholesky는 잘 작동하며 이것은 실제로 "내 코드에서 버그를 찾을 수 있습니까?"유형 질문입니다. 질문의 제목과 내용은 원래 쓰여졌 듯이 기본적으로 "Cholesky가 작동하지 않습니다. 대안은 무엇입니까?" 이 사이트를 검색하는 사용자에게는 매우 혼란 스러울 것입니다. 이 질문을 반영하여 편집해야합니까? (약점은 javlacalle의 답변이 관련성이 적다는 것입니다. 단점은 질문 텍스트가 검색자가 실제로 페이지에서 찾은 것을 반영하는 것입니다.)
Silverfish

@Antoni Parellada 예, 파이썬 numpy로 올바른 방법으로 MATLAB 코드를 변환했다고 생각합니다. OP의 잘못된 코드를 MATLAB과 동등한 코드로 이미 변환했으며 그의 잘못된 결과를 복제했습니다.
Mark L. Stone

답변:


11

Cholesky 분해를 기반으로 한 접근 방식이 작동해야하며 여기 에 설명 되어 있으며 Mark L. Stone의 답변에이 답변과 거의 동시에 게시되었습니다.

그럼에도 불구하고 때로는 다음과 같이 다변량 정규 분포 에서 그리기를 생성했습니다 .(μ,Σ)

와이=엑스+μ,=Λ1/2Φ,

여기서 는 최종 추첨이고 는 일 변량 표준 정규 분포에서 추첨됩니다. 는 대상 행렬 의 정규화 된 고유 벡터를 포함하는 행렬 이고 는 동일한 순서로 배열 된 의 고유 값을 포함하는 대각 행렬입니다. 열의 고유 벡터로 .와이엑스ΦΣΛΣΦ

의 예 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로 상관 관계를 확인하여 스퓨리어스 상관 관계를 먼저 확인하십시오.
Gottfried Helms

17

코드 대신 단어와 대수로 수행 한 작업을 설명하거나 적어도 의사 코드를 사용하여 작성하면 사람들이 오류를 훨씬 빨리 발견 할 수 있습니다.

당신은 이것과 동등한 것을하고있는 것처럼 보입니다 (전혀 가능할지라도) :

  1. 표준 법선 의 행렬 생성 ,×케이

  2. 곱에 의해 열 및 추가는 비표준 법선을 얻을 수 있습니다σ나는μ나는

  3. 상관 된 법선을 얻기 위해 를 계산합니다 .와이=엑스

여기서 은 상관 행렬의 왼쪽 Cholesky 계수입니다.

당신이해야 할 일은 이것입니다 :

  1. 표준 법선 의 행렬 생성 ,×케이

  2. 상관 법선을 얻기 위해 를 계산합니다 .엑스=

  3. 곱에 의해 열 및 추가는 비표준 법선을 얻을 수 있습니다σ나는μ나는

이 알고리즘에 대한 많은 설명이 사이트에 있습니다. 예 :

상관 난수를 생성하는 방법 (제공된 평균, 분산 및 상관 정도)?

주어진 평균으로 상관 랜덤 변수를 생성하기 위해 Cholesky-method를 사용할 수 있습니까?

이것은 원하는 공분산 행렬과 관련하여 직접 논의하고 원하는 샘플 공분산 을 얻는 알고리즘을 제공합니다 .

주어진 표본 공분산 행렬로 데이터 생성


11

hole 레 스키 인수 분해에는 아무런 문제가 없습니다. 코드에 오류가 있습니다. 아래 편집을 참조하십시오.

MATLAB 코드와 결과는 다음과 같습니다. 먼저 n_obs = 10000, n_obs = 1e8입니다. 간단하게, 결과에 영향을 미치지 않기 때문에 나는 수단을 귀찮게하지 않습니다. 즉, 그것들을 0으로 만듭니다. MATLAB의 chol은 행렬 M의 상위 삼각 Cholesky 계수 R을 생성하여 R '* R = M입니다. numpy.linalg.cholesky는 하위 삼각 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의 기여를 어떻게 돌 볼지 궁금했습니다. 질문에 대한 실제 답변은 자신의 게시물에 제공됩니다 (의심이있을 경우 자신의 게시물에 크레딧을 제공하십시오). 앞으로이 게시물을 쉽게 검색 할 수 있도록이 추가 정보를 여기로 옮기고 있습니다. Mark의 답변 후에 다른 우수 답변을 재생하지 않고 OP의 게시물을 수정하여 문제를 마무리합니다.

출처

PYTHON에서 :

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.        ]]

[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

다른 사람들이 이미 보여준 것처럼 : cholesky works. 여기에 의사 코드에 매우 짧고 매우 가까운 코드가 있습니다 : 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.