Cách sử dụng phân tách Cholesky hoặc thay thế cho mô phỏng dữ liệu tương quan


19

Tôi sử dụng phân tách Cholesky để mô phỏng các biến ngẫu nhiên tương quan được đưa ra một ma trận tương quan. Điều này là, kết quả không bao giờ tái tạo cấu trúc tương quan như nó được đưa ra. Dưới đây là một ví dụ nhỏ trong Python để minh họa tình huống.

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))) 

Bản in này:

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

Như bạn có thể thấy, ma trận tương quan ước tính sau hoc khác rất nhiều so với ma trận trước. Có một lỗi trong mã của tôi, hoặc có một số thay thế cho việc sử dụng phân tách Cholesky?

Chỉnh sửa

Tôi xin lỗi vì sự lộn xộn này. Tôi đã không nghĩ rằng có một lỗi trong mã và / hoặc theo cách phân tách Cholesky được áp dụng do một số hiểu lầm về tài liệu tôi đã nghiên cứu trước đây. Trong thực tế, tôi chắc chắn rằng phương pháp đó không có nghĩa là chính xác và tôi đã ổn với điều đó cho đến khi tình huống khiến tôi đăng câu hỏi này. Cảm ơn bạn đã chỉ ra quan niệm sai lầm tôi đã có. Tôi đã chỉnh sửa tiêu đề để phản ánh tốt hơn tình hình thực tế theo đề xuất của @Silverfish.


1
Cholesky hoạt động tốt, và đây thực sự là một câu hỏi kiểu "bạn có thể tìm thấy lỗi trong mã của tôi" không. Tiêu đề và nội dung của câu hỏi, như được viết ban đầu, về cơ bản là "Cholesky không hoạt động, có gì thay thế"? Điều đó sẽ rất khó hiểu với người dùng đang tìm kiếm trang web này. Câu hỏi này có nên được chỉnh sửa để phản ánh điều này? (Nhược điểm là câu trả lời của javlacalle sẽ ít liên quan hơn. Mặt trái là văn bản câu hỏi sau đó sẽ phản ánh những gì người tìm kiếm thực sự sẽ tìm thấy trên trang.)
Silverfish

@Antoni Parellada Vâng, tôi nghĩ rằng bạn đã dịch mã MATLAB của tôi cho cách (a) chính xác để làm điều đó thành Python numpy, hoàn thành với sự điều chỉnh cho np.linalg.cholesky là tam giác thấp hơn so với MATLAB là tam giác trên. Tôi đã dịch mã không chính xác của OP thành MATLAB tương đương và sao chép kết quả không chính xác của anh ấy.
Mark L. Stone

Câu trả lời:


11

Cách tiếp cận dựa trên phân tách Cholesky sẽ hoạt động, nó được mô tả ở đây và được hiển thị trong câu trả lời của Mark L. Stone được đăng gần như cùng lúc với câu trả lời này.

N(μ,Σ)

Y= =QX+μ,vớiQ= =Λ1/2Φ,

YXΦΣΛΣΦ

Ví dụ trong R(xin lỗi tôi không sử dụng cùng một phần mềm bạn đã sử dụng trong câu hỏi):

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

Bạn cũng có thể quan tâm đến bài viết nàybài đăng này .


Để làm cho ma trận tương quan được sao chép chính xác, người ta phải loại bỏ các mối tương quan giả trong dữ liệu ngẫu nhiên khỏi trình tạo ngẫu nhiên trước khi áp dụng nó vào quy trình tạo dữ liệu. Ví dụ, kiểm tra mối tương quan của dữ liệu ngẫu nhiên của bạn trong eps để xem mối tương quan giả đó trước tiên.
Gottfried Helms

17

Mọi người có thể sẽ tìm thấy lỗi của bạn nhanh hơn nhiều nếu bạn giải thích những gì bạn đã làm với các từ và đại số thay vì mã (hoặc ít nhất là viết nó bằng mã giả).

Bạn dường như đang làm tương đương với điều này (mặc dù có thể chuyển đổi):

  1. n×kZ

  2. σtôiμtôi

  3. Y= =LX

L

Những gì bạn nên làm là đây:

  1. n×kZ

  2. X= =LZ

  3. σtôiμtôi

Có nhiều giải thích về thuật toán này trên trang web. ví dụ

Làm thế nào để tạo ra các số ngẫu nhiên tương quan (phương tiện, phương sai và mức độ tương quan)?

Tôi có thể sử dụng phương pháp Cholesky để tạo các biến ngẫu nhiên tương quan với giá trị trung bình đã cho không?

Điều này thảo luận trực tiếp về mặt ma trận hiệp phương sai mong muốn và cũng đưa ra một thuật toán để có được hiệp phương thức mẫu mong muốn :

Tạo dữ liệu với ma trận hiệp phương sai mẫu đã cho


11

Không có gì sai với yếu tố Cholesky. Có một lỗi trong mã của bạn. Xem chỉnh sửa dưới đây.

Đây là mã MATLAB và kết quả, đầu tiên cho n_obs = 10000 như bạn có, sau đó cho n_obs = 1e8. Để đơn giản, vì nó không ảnh hưởng đến kết quả, tôi không bận tâm đến phương tiện, tức là tôi biến chúng thành số không. Lưu ý rằng chol của MATLAB tạo ra hệ số Cholesky tam giác trên R của ma trận M sao cho R '* R = M. numpy.linalg.cholesky tạo ra hệ số Cholesky tam giác thấp hơn, do đó cần phải điều chỉnh so với mã của tôi; nhưng tôi tin rằng mã của bạn là tốt về mặt đó.

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

Chỉnh sửa: Tôi tìm thấy sai lầm của bạn. Bạn áp dụng sai độ lệch chuẩn. Điều này tương đương với những gì bạn đã làm, đó là sai.

   >> 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 không phải là về mã, nhưng tôi rất tò mò muốn xem nó sẽ trông như thế nào sau tất cả các câu trả lời hay, và cụ thể là đóng góp của @Mark L. Stone. Câu trả lời thực tế cho câu hỏi được cung cấp trên bài đăng của anh ấy (vui lòng ghi có bài đăng của anh ấy trong trường hợp nghi ngờ). Tôi đang chuyển thông tin được nối thêm này vào đây để tạo điều kiện truy xuất bài đăng này trong tương lai. Không cần phát lại bất kỳ câu trả lời xuất sắc nào khác, sau câu trả lời của Mark, điều này kết thúc vấn đề bằng cách sửa bài đăng trong OP.

Nguồn

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

VÀO [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

Như những người khác đã chỉ ra: tác phẩm cholesky. Đây là một đoạn mã rất ngắn và rất gần mã giả: một mật mã trong 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.