Come generare in modo efficiente matrici di correlazione semidefinite positive casuali?


38

Vorrei essere in grado di generare in modo efficiente matrici di correlazione semidefinite positiva (PSD). Il mio metodo rallenta notevolmente man mano che aumento le dimensioni delle matrici da generare.

  1. Potresti suggerire soluzioni efficaci? Se siete a conoscenza di esempi in Matlab, sarei molto grato.
  2. Quando generi una matrice di correlazione PSD come sceglieresti i parametri per descrivere le matrici da generare? Una correlazione media, deviazione standard delle correlazioni, autovalori?

Risposte:


16

Puoi farlo all'indietro: ogni matrice CR++p (l'insieme di tutte le matrici simmetriche p×p PSD) può essere scomposta come

C=OTDO dove è una matrice ortonormaleO

Per ottenere , prima genera una base casuale (dove sono vettori casuali, in genere in ). Da lì, utilizzare il processo di ortogonalizzazione di Gram-Schmidt per ottenereO(v1,...,vp)vi(1,1)(u1,....,up)=O

R ha un numero di pacchetti che può eseguire l'ortogonalizzazione GS di una base casuale in modo efficiente, vale a dire anche per grandi dimensioni, ad esempio il pacchetto "lontano". Sebbene troverai l'algoritmo GS su wiki, probabilmente è meglio non reinventare la ruota e fare un'implementazione di matlab (ne esiste sicuramente una, non posso proprio consigliarne nessuna).

Infine, è una matrice diagonale i cui elementi sono tutti positivi (questo è, ancora una volta, facile da generare: generare numeri casuali , quadrarli, ordinarli e posizionarli sulla diagonale di un'identità per matrice ).p p pDppp


3
(1) Notare che la risultante non sarà una matrice di correlazione (come richiesto dall'OP), perché non ne avrà una sulla diagonale. Naturalmente può essere ridotta alle avere quelle sulla diagonale, impostandola E - 1 / 2 C E - 1 / 2 , dove E è una matrice diagonale con la stessa diagonale come C . (2) Se non sbaglio, questo si tradurrà in matrici di correlazione in cui tutti gli elementi off-diagonali sono concentrati intorno a 0 , quindi non c'è flessibilità che l'OP stava cercando (l'OP voleva essere in grado di impostare "una correlazione media , deviazione standard delle correlazioni, autovalori "CE-1/2CE-1/2EC0)
ameba dice di reintegrare Monica il

@amoeba: mi rivolgerò a (2) poiché, come fai notare, la soluzione a (1) è banale. La caratterizzazione a un numero della "forma" (la relazione tra gli elementi diagonali in e out) di una matrice PSD (e quindi una covarianza e anche una matrice di correlazione) è il suo numero di condizione. E, il metodo sopra descritto consente il pieno controllo su di esso. La "concentrazione degli elementi fuori diagonale attorno a 0" non è una caratteristica del metodo utilizzato per generare matrici di PSD ma, piuttosto, una conseguenza dei vincoli necessari per garantire che la matrice sia PSD e che sia grande. p
user603

Stai dicendo che tutte le grandi matrici PSD hanno elementi fuori diagonale vicini allo zero? Non sono d'accordo, non è così. Controlla qui la mia risposta per alcuni esempi: Come generare una matrice di correlazione casuale che ha voci fuori diagonale distribuite approssimativamente normalmente con una deviazione standard determinata? Ma si può vedere direttamente che non è così, perché una matrice quadrata che ha tutti quelli sulla diagonale e un valore fisso ovunque fuori diagonale è PSD e ρ può essere arbitrariamente grande (ma ovviamente inferiore a 1 ). ρρ1
ameba dice di reintegrare Monica il

@amoeba: allora ho sbagliato a supporre che per necessità le off diagonali di grandi matrici di correlazione quando possono essere sia positive sia negative sono vicine a 0. Grazie per l'esempio illuminante.
user603

1
Ho letto un bellissimo documento sulla generazione di matrici di correlazione casuali e ho fornito la mia risposta qui (così come un'altra risposta in quel thread collegato). Penso che potresti trovarlo interessante.
ameba dice Ripristina Monica il

27

Un articolo sulla generazione di matrici di correlazione casuali basate su viti e metodo a cipolla estesa di Lewandowski, Kurowicka e Joe (LKJ), 2009, fornisce un trattamento unificato e l'esposizione dei due metodi efficienti per generare matrici di correlazione casuali. Entrambi i metodi consentono di generare matrici da una distribuzione uniforme in un certo senso definito di seguito, sono semplici da implementare, veloci e hanno un ulteriore vantaggio di avere nomi divertenti.

Una vera matrice simmetrica di dimensioni con quelle sulla diagonale ha d ( d - 1 ) / 2 elementi off-diagonali unici e quindi può essere parametrizzata come punto in R d ( d - 1 ) / 2 . Ogni punto in questo spazio corrisponde a una matrice simmetrica, ma non tutti sono definiti in modo positivo (come devono essere le matrici di correlazione). Le matrici di correlazione formano quindi un sottoinsieme di R d ( d - 1 ) / 2d×dd(d-1)/2Rd(d-1)/2Rd(d-1)/2 (in realtà un sottoinsieme convesso collegato) ed entrambi i metodi possono generare punti da una distribuzione uniforme su questo sottoinsieme.

Fornirò la mia implementazione MATLAB di ciascun metodo e li illustrerò con .d=100


Metodo di cipolla

Il metodo onion deriva da un altro documento (ref # 3 in LKJ) e possiede il suo nome sul fatto che le matrici di correlazione vengono generate a partire da una matrice e crescendo colonna per colonna e riga per riga. La distribuzione risultante è uniforme. Non capisco davvero la matematica dietro il metodo (e preferisco comunque il secondo metodo), ma ecco il risultato:1×1

Metodo di cipolla

Qui e sotto il titolo di ogni sottotrama mostra gli autovalori più piccoli e più grandi e il determinante (prodotto di tutti gli autovalori). Ecco il codice:

%// ONION METHOD to generate random correlation matrices distributed randomly
function S = onion(d)
    S = 1;
    for k = 2:d
        y = betarnd((k-1)/2, (d-k)/2); %// sampling from beta distribution
        r = sqrt(y);
        theta = randn(k-1,1);
        theta = theta/norm(theta);
        w = r*theta;
        [U,E] = eig(S);
        R = U*E.^(1/2)*U';             %// R is a square root of S
        q = R*w;
        S = [S q; q' 1];               %// increasing the matrix size
    end
end

Metodo di cipolla estesa

LKJ modifica leggermente questo metodo, al fine di poter campionare le matrici di correlazione da una distribuzione proporzionale a [ d e tC . Più grande è il η , maggiore sarà il determinante, il che significa che le matrici di correlazione generate si avvicinano sempre più alla matrice dell'identità. Il valore η = 1 corrisponde a una distribuzione uniforme. Nella figura seguente le matrici sono generate con η = 1 , 10 , 100 , 1000 , 10[detC]η-1ηη=1 .η=1,10,100,1000,10000,100000

Metodo di cipolla estesa

Per qualche motivo per ottenere il determinante dello stesso ordine di grandezza del metodo della cipolla vanigliata, devo mettere e non η = 1 (come sostenuto da LKJ). Non sono sicuro di dove sia l'errore.η=0η=1

%// EXTENDED ONION METHOD to generate random correlation matrices
%// distributed ~ det(S)^eta [or maybe det(S)^(eta-1), not sure]
function S = extendedOnion(d, eta)
    beta = eta + (d-2)/2;
    u = betarnd(beta, beta);
    r12 = 2*u - 1;
    S = [1 r12; r12 1];  

    for k = 3:d
        beta = beta - 1/2;
        y = betarnd((k-1)/2, beta);
        r = sqrt(y);
        theta = randn(k-1,1);
        theta = theta/norm(theta);
        w = r*theta;
        [U,E] = eig(S);
        R = U*E.^(1/2)*U';
        q = R*w;
        S = [S q; q' 1];
    end
end

Metodo della vite

Il metodo Vine è stato originariamente suggerito da Joe (J in LKJ) e migliorato da LKJ. Mi piace di più, perché è concettualmente più semplice e anche più facile da modificare. L'idea è di generare correlazioni parziali (sono indipendenti e possono avere qualsiasi valore da [ - 1 , 1 ]d(d1)/2[1,1]senza alcun vincolo) e poi convertirli in correlazioni grezze tramite una formula ricorsiva. È conveniente organizzare il calcolo in un certo ordine e questo grafico è noto come "vite". È importante sottolineare che, se vengono campionate correlazioni parziali da particolari distribuzioni beta (diverse per celle diverse nella matrice), la matrice risultante verrà distribuita uniformemente. Anche in questo caso, LKJ introduce un ulteriore parametro per campionare da una distribuzione proporzionale a [ d e tη . Il risultato è identico alla cipolla estesa:[detC]η1

Metodo della vite

%// VINE METHOD to generate random correlation matrices
%// distributed ~ det(S)^eta [or maybe det(S)^(eta-1), not sure]
function S = vine(d, eta)
    beta = eta + (d-1)/2;   
    P = zeros(d);           %// storing partial correlations
    S = eye(d);

    for k = 1:d-1
        beta = beta - 1/2;
        for i = k+1:d
            P(k,i) = betarnd(beta,beta); %// 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
end

Metodo della vite con campionamento manuale di correlazioni parziali

Come si può vedere sopra, la distribuzione uniforme si traduce in matrici di correlazione quasi diagonali. Ma si può facilmente modificare il metodo della vite per avere correlazioni più forti (questo non è descritto nel documento LKJ, ma è semplice): per questo si dovrebbero campionare correlazioni parziali da una distribuzione concentrata attorno a . Di seguito li campiono dalla distribuzione beta (riscalato da [ 0 , 1 ] a [ - 1 , 1 ] ) con α = β = 50 , 20 , 10 , 5 , 2 , 1±1[0,1][1,1]α=β=50,20,10,5,2,1. Più piccoli sono i parametri della distribuzione beta, più è concentrata vicino ai bordi.

Metodo della vite con campionamento manuale

Si noti che in questo caso non è garantito che la distribuzione sia invariante per permutazione, quindi permetto anche casualmente righe e colonne dopo la generazione.

%// VINE METHOD to generate random correlation matrices
%// with all partial correlations distributed ~ beta(betaparam,betaparam)
%// rescaled to [-1, 1]
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

Ecco come gli istogrammi degli elementi off-diagonali cercano le matrici sopra (la varianza della distribuzione aumenta monotonicamente):

Elementi fuori diagonale


Aggiornamento: utilizzando fattori casuali

k<dWk×dWWDB=WW+DC=E-1/2BE-1/2EBK=100,50,20,10,5,1

matrici di correlazione casuale da fattori casuali

E il codice:

%// FACTOR method
function S = factor(d,k)
    W = randn(d,k);
    S = W*W' + diag(rand(1,d));
    S = diag(1./sqrt(diag(S))) * S * diag(1./sqrt(diag(S)));
end

Ecco il codice di wrapping utilizzato per generare le figure:

d = 100; %// size of the correlation matrix

figure('Position', [100 100 1100 600])
for repetition = 1:6
    S = onion(d);

    %// etas = [1 10 100 1000 1e+4 1e+5];
    %// S = extendedOnion(d, etas(repetition));

    %// S = vine(d, etas(repetition));

    %// betaparams = [50 20 10 5 2 1];
    %// S = vineBeta(d, betaparams(repetition));

    subplot(2,3,repetition)

    %// use this to plot colormaps of S
    imagesc(S, [-1 1])
    axis square
    title(['Eigs: ' num2str(min(eig(S)),2) '...' num2str(max(eig(S)),2) ', det=' num2str(det(S),2)])

    %// use this to plot histograms of the off-diagonal elements
    %// offd = S(logical(ones(size(S))-eye(size(S))));
    %// hist(offd)
    %// xlim([-1 1])
end

2
Questa è una carrellata fantastica, sono contento di aver detto qualcosa!
Shadowtalker,

Quando ho tradotto il codice matlab per la matrice di correlazione a base di vite in R e l'ho testato, la densità delle correlazioni nella colonna 1 era sempre diversa dalle colonne successive. È possibile che abbia tradotto qualcosa in modo errato, ma forse questa nota aiuta qualcuno.
Charlie,

3
Per gli utenti R, la funzione rcorrmatrix nel pacchetto clusterGeneration (scritto da W Qui e H. Joe) implementa il metodo vine.
RNM,

15

UNUNTUNyT(UNTUN)y0yyT(UNTUN)y=(UNy)TUNy=||UNy||che non è negativo. Quindi in Matlab, prova semplicemente

A = randn(m,n);   %here n is the desired size of the final matrix, and m > n
X = A' * A;

A seconda dell'applicazione, ciò potrebbe non fornire la distribuzione degli autovalori desiderati; La risposta di Kwak è molto migliore in questo senso. Gli autovalori Xprodotti da questo frammento di codice dovrebbero seguire la distribuzione Marchenko-Pastur.

Per simulare le matrici di correlazione degli stock, diciamo, potresti voler un approccio leggermente diverso:

k = 7;      % # of latent dimensions;
n = 100;    % # of stocks;
A = 0.01 * randn(k,n);  % 'hedgeable risk'
D = diag(0.001 * randn(n,1));   % 'idiosyncratic risk'
X = A'*A + D;
ascii_hist(eig(X));    % this is my own function, you do a hist(eig(X));
-Inf <= x <  -0.001 : **************** (17)
-0.001 <= x <   0.001 : ************************************************** (53)
 0.001 <= x <   0.002 : ******************** (21)
 0.002 <= x <   0.004 : ** (2)
 0.004 <= x <   0.005 :  (0)
 0.005 <= x <   0.007 : * (1)
 0.007 <= x <   0.008 : * (1)
 0.008 <= x <   0.009 : *** (3)
 0.009 <= x <   0.011 : * (1)
 0.011 <= x <     Inf : * (1)

1
saresti disposto a condividere la tua funzione ascii_hist per caso?
btown

@btown il margine è troppo piccolo per contenerlo!
Shabbychef,

1
yT(UNTUN)y=(UNy)TUNy=||UNy||

8

DUN=QDQTQ


M.: Bel riferimento: questa sembra essere la soluzione più efficiente (asintoticamente).
whuber

3
@whuber: Heh, l'ho raccolto da Golub e Van Loan (ovviamente); Lo uso sempre per aiutare a generare matrici di test per autovalutazione / routine di valori singolari. Come si può vedere dalla carta, è essenzialmente equivalente alla decomposizione QR di una matrice casuale come quella suggerita da Kwak, tranne per il fatto che viene eseguita in modo più efficiente. C'è un'implementazione MATLAB di questo nella Text Matrix Toolbox di Higham, BTW.
JM non è uno statistico il

M.:> Grazie per l'implementazione di matlab. Sapresti per caso di un generatore di matrice pseudo-casuale di Haar in R?
user603

@kwak: Non ne ho idea, ma se non c'è ancora l'implementazione, non dovrebbe essere troppo difficile tradurre il codice MATLAB in R (posso provare a frustarne uno se non ce ne sono davvero); l'unico prerequisito è un generatore decente per variate pseudocasuali normali, che sono sicuro che R abbia.
JM non è uno statistico il

M.:> Sì, probabilmente lo tradurrò da solo. Grazie per i collegamenti, il migliore.
user603,

4

Non hai specificato una distribuzione per le matrici. Due comuni sono le distribuzioni Wishart e inverse Wishart. La decomposizione di Bartlett fornisce una fattorizzazione di Cholesky di una matrice Wishart casuale (che può anche essere risolta in modo efficiente per ottenere una matrice Wishart inversa casuale).

In effetti, lo spazio Cholesky è un modo conveniente per generare altri tipi di matrici PSD casuali, poiché devi solo assicurarti che la diagonale non sia negativa.


> Non casuale: due matrici generate dallo stesso Whishard non saranno indipendenti l'una dall'altra. Se hai intenzione di cambiare il Whishart ad ogni generazione, allora come intendi generare quei Whishart in primo luogo?
user603

@kwak: Non capisco la tua domanda: la scomposizione di Bartlett darà estrazioni indipendenti dalla stessa distribuzione di Wishart.
Simon Byrne,

> Lasciami riformulare questo, da dove prendi la matrice di scala della tua distribuzione di whishart?
user603

1
@kwak: è un parametro della distribuzione e quindi è stato risolto. Lo selezioni all'inizio, in base alle caratteristiche desiderate della tua distribuzione (come la media).
Simon Byrne,

3

UTSU


Se le voci sono generate da una distribuzione normale piuttosto che da una divisa, la decomposizione che menzioni dovrebbe essere invariante SO (n) (e quindi equidistribuita rispetto alla misura di Haar).
whuber

Interessante. Potete fornire un riferimento per questo?
gappy,

1
> il problema con questo metodo è che non puoi controllare il rapporto tra autovalore più piccolo e più grande (e penso che poiché la dimensione del tuo set di dati generato casualmente va all'infinito, questo rapporto converge a 1).
user603

1

Se desideri avere un maggiore controllo sulla matrice PSD simmetrica generata, ad esempio generare un set di dati di convalida sintetico, hai a disposizione una serie di parametri. Una matrice PSD simmetrica corrisponde a un'iperellisse nello spazio N-dimensionale, con tutti i relativi gradi di libertà:

  1. Rotazioni.
  2. Lunghezze degli assi.

Quindi, per una matrice bidimensionale (ovvero 2 ellissi), avrai 1 rotazione + 2 assi = 3 parametri.

Se le rotazioni richiamano alla mente le matrici ortogonali, è comunque un treno corretto, poiché la costruzione è di nuovo Σ=ODOT, con Σ essendo la matrice Sym.PSD prodotta, O la matrice di rotazione (che è ortogonale) e Dla matrice diagonale, i cui elementi diagonali controlleranno la lunghezza degli assi dell'ellisse.

Il seguente codice Matlab traccia 16 set di dati distribuiti gaussiani bidimensionali in base a Σ, con un angolo crescente. Il codice per la generazione casuale di parametri è nei commenti.

figure;
mu = [0,0];
for i=1:16
    subplot(4,4,i)
    theta = (i/16)*2*pi;   % theta = rand*2*pi;
    U=[cos(theta), -sin(theta); sin(theta) cos(theta)];
    % The diagonal's elements control the lengths of the axes
    D = [10, 0; 0, 1]; % D = diag(rand(2,1));    
    sigma = U*D*U';
    data = mvnrnd(mu,sigma,1000);
    plot(data(:,1),data(:,2),'+'); axis([-6 6 -6 6]); hold on;
end

Per più dimensioni, la matrice diagonale è semplice (come sopra) e la U dovrebbe derivare dalla moltiplicazione delle matrici di rotazione.


0

Un approccio economico e allegro che ho usato per i test è di generare m N (0,1) n-vettori V [k] e quindi usare P = d * I + Somma {V [k] * V [k] '} come matrice nxn psd. Con m <n questo sarà singolare per d = 0 e per piccolo d avrà un numero di condizione elevato.


2
> il problema con questo metodo è che non puoi controllare il rapporto tra autovalore più piccolo e più grande (e penso che poiché la dimensione del tuo set di dati generato casualmente va all'infinito, questo rapporto converge a 1).
user603

> inoltre, il metodo non è molto efficiente (dal punto di vista computazionale)
user603

1
La tua "matrice casuale" è una matrice appositamente strutturata chiamata "matrice diagonale più rango 1" (matrice DR1), quindi non è una matrice casuale rappresentativa.
JM non è uno statistico il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.