Os questão diz respeito a como gerar variates aleatória de uma distribuição normal com uma multivariada (possivelmente) singular matriz de covariância . Esta resposta explica uma maneira que funcionará para qualquer matriz de covariância. Ele fornece uma implementação que testa sua precisão.CR
Análise algébrica da matriz de covariância
Como é uma matriz de covariância, é necessariamente simétrica e semidefinida positiva. Para completar a informação de fundo, vamos μ ser o vetor de meio desejado.Cμ
Como é simétrico, sua Decomposição de Valor Singular (SVD) e sua composição automática terão automaticamente a formaC
C=VD2V′
para algumas matrizes ortogonais e matrizes diagonais D 2 . Em geral, os elementos diagonais de D 2 não são negativos (o que implica que todos têm raízes quadradas reais: escolha os positivos para formar a matriz diagonal D ). As informações que temos sobre C dizem que um ou mais desses elementos diagonais são zero - mas isso não afetará nenhuma das operações subseqüentes nem impedirá que o SVD seja calculado.VD2D2DC
Gerando valores aleatórios multivariados
Let tem uma distribuição normal multivariada padrão: cada componente tem média zero, variância unidade e todos os covariâncias são zero: a sua matriz de covariância é a identidade que eu . Então a variável aleatória Y = V D X tem matriz de covariânciaXIY=VDX
Cov( Y) = E ( YY′) = E ( V D XX′D′V′) = V D E ( XX′) D V′=VDIDV′=VD2V′=C.
Por conseguinte, a variável aleatória tem uma distribuição normal com média multivariada μ e matriz de covariância C .μ+YμC
Código de exemplo e computação
O R
código a seguir gera uma matriz de covariância de determinadas dimensões e classificações, analisa-a com o SVD (ou, em código comentado, com uma composição de eigend), usa essa análise para gerar um número especificado de realizações de (com vetor médio 0 ) e, em seguida, compara a matriz de covariância desses dados com a matriz de covariância pretendida, tanto numérica como graficamente. Como mostrado, gera 10 ,Y0 realizações em que a dimensão de Y é de 100 e a patente de C é 50 . A saída é10,000Y100C50
rank L2
5.000000e+01 8.846689e-05
Ou seja, a classificação dos dados também é e a matriz de covariância estimada a partir dos dados está à distância50 de C --que está próximo. Como uma verificação mais detalhada, os coeficientes de C são plotados contra os de sua estimativa. Todos estão próximos da linha da igualdade:8×10−5CC
O código é exatamente paralelo à análise anterior e, portanto, deve ser autoexplicativo (mesmo para não R
usuários, que podem emular no seu ambiente de aplicativo favorito). Uma coisa que revela é a necessidade de cautela ao usar algoritmos de ponto flutuante: as entradas de podem ser facilmente negativas (mas minúsculas) devido à imprecisão. Tais entradas precisam ser zeradas antes de calcular a raiz quadrada para encontrar D em si.D2D
n <- 100 # Dimension
rank <- 50
n.values <- 1e4 # Number of random vectors to generate
set.seed(17)
#
# Create an indefinite covariance matrix.
#
r <- min(rank, n)+1
X <- matrix(rnorm(r*n), r)
C <- cov(X)
#
# Analyze C preparatory to generating random values.
# `zapsmall` removes zeros that, due to floating point imprecision, might
# have been rendered as tiny negative values.
#
s <- svd(C)
V <- s$v
D <- sqrt(zapsmall(diag(s$d)))
# s <- eigen(C)
# V <- s$vectors
# D <- sqrt(zapsmall(diag(s$values)))
#
# Generate random values.
#
X <- (V %*% D) %*% matrix(rnorm(n*n.values), n)
#
# Verify their covariance has the desired rank and is close to `C`.
#
s <- svd(Sigma <- cov(t(X)))
(c(rank=sum(zapsmall(s$d) > 0), L2=sqrt(mean(Sigma - C)^2)))
plot(as.vector(C), as.vector(Sigma), col="#00000040",
xlab="Intended Covariances",
ylab="Estimated Covariances")
abline(c(0,1), col="Gray")