Як генерувати велику повнорозмірну випадкову кореляційну матрицю з наявними сильними кореляціями?


25

Я хотів би генерувати випадкову кореляційну матрицю розміром таким чином, щоб були наявні помірно сильні кореляції: n × nCn×n

  • квадратна реальна симетрична матриця розміру, наприклад ;n = 100n×nn=100
  • позитивне-визначене, тобто з усіма власними значеннями реальними та позитивними;
  • повний ранг;
  • всі діагональні елементи рівні ;1
  • недіагональні елементи повинні бути досить рівномірно розподілені на . Точний розподіл не має значення, але я хотів би мати деяку помірно велику кількість (наприклад, ) помірно великих значень (наприклад, з абсолютним значенням або вище). В основному я хочу , щоб переконатися , що зробити є НЕ майже по діагоналі з усіма недіагональні елементами .10 % 0,5 C0(1,1)10%0.5C0

Чи є простий спосіб це зробити?

Метою є використання таких випадкових матриць для порівняння деяких алгоритмів, що працюють з кореляційними (або коваріаційними) матрицями.


Методи, які не працюють

Ось кілька способів генерування випадкових матриць кореляції, про які я знаю, але які для мене тут не працюють:

  1. Генерація випадкових з розміру, в центрі, стандартизувати і утворюють матрицю кореляції . Якщо , це, як правило, призводить до того, що всі позадіагональні кореляції будуть приблизно . Якщо , деякі кореляції будуть сильними, але не буде повним рангом. s × n C = 1Xs×ns>n0snCC=1s1XXs>n0snC

  2. Створити випадкову позитивну певну матрицю одним із наступних способів:B

    • Утворіть випадковий квадрат і зробіть симетричний позитивний визначений .B = A AAB=AA

    • Створіть випадковий квадрат , зробіть симетричний , і зробіть його позитивним визначеним, виконуючи власне розкладання і встановлення всіх негативних власних значень до нуля: . Примітка: це призведе до дефіцитної матриці.E = A + AE = U S UB = UAE=A+AE=USUB=Umax{S,0}U

    • Створити випадковий ортогональний (наприклад, генеруючи випадковий квадрат і здійснюючи його QR-розкладання, або за допомогою процесу Грам-Шмідта) та випадкову діагональ з усіма позитивними елементами; форма .A D B = Q D QQADB=QDQ

    Отриману матрицю можна легко нормалізувати, щоб мати всі діагоналі: , де є діагональною матрицею з тією ж діагоналлю , як . Усі три перераховані вище способи для генерації призводять до того, що має недіагональні елементи, близькі .З = Д - 1 / 2 Б Д - 1 / 2 D = D я гBC=D1/2BD1/2B B C 0D=diagBBBC0


Оновлення: старіші теми

Опублікувавши своє запитання, я виявив два майже копії в минулому:

На жаль, жодна з цих тем не містила задовільної відповіді (дотепер :)


1
Ви можете створити випадкову ортогональну матрицю методами QR або Грама-Шмідта. Це будуть "власні вектори PCA". Додайте масштаб до своїх стовпців (перетворіться на "завантаження"). Отримайте коваріаційну матрицю з цих навантажень. Щось подібне ...
ttnphns

1
Ум, добре .. Уявіть, що ми хочемо створити nXkзавантажувальну матрицю W, не повністю випадкову, а ту, яку ми хочемо (вона буде WW'+diag(noise)визначати матрицю cov, яку ми шукаємо. Єдине завдання - виправити нормований на стовпчик W (тобто k "власні вектори") стають ортогональними. Будь-який метод
декореляції

1
Ах, @whuber, тепер я бачу, що ти маєш на увазі. Так, ви маєте рацію: якщо всі позадіагональні елементи однакові і рівні , матриця справді є повноцінною і позитивно визначеною ... Це, звичайно, не те, що я мав на увазі: я хотів би розподілу недіагональних елементів у кожній матриці, щоб бути розумно "розповсюдженими", а не розподілом по матрицях ...ρ
Амеба каже Reinstate Monica

3
Ви можете заглянути в дистрибутив
LKJ

2
@ttnphns: Я думаю, що нарешті зрозумів, що ти був правильний весь час: те, що ти запропонував, - це найпростіший спосіб досягти мети. Я додав оновлення до своєї відповіді, реалізуючи по суті те, що ви написали вище.
амеба повідомляє Відновити Моніку

Відповіді:


14

Інші відповіді придумали приємні хитрощі, щоб вирішити мою проблему різними способами. Однак я знайшов принциповий підхід, який, на мою думку, має велику перевагу в тому, що концептуально дуже чіткий і простий у налаштуванні.

У цій темі: Як ефективно генерувати випадкові матриці кореляції позитиву-напівдефініта? - Я описав і надав код для двох ефективних алгоритмів генерації випадкових кореляційних матриць. І те й інше походять з доповіді Левандовського, Куровіки та Джо (2009), про яку @ssdecontrol згадувалося у коментарях вище (велике спасибі!).

Будь ласка, дивіться мою відповідь там, щоб отримати багато цифр, пояснень та код matlab. Так званий метод "лоза" дозволяє генерувати випадкові матриці кореляції з будь-яким розподілом часткових кореляцій і може використовуватися для генерації кореляційних матриць з великими позадіагональними значеннями. Ось приклад з цієї теми:

Виноградний метод

Єдине, що змінюється між субплотами, - це один параметр, який контролює, наскільки сконцентрований розподіл часткових кореляцій навколо .±1

Я копіюю свій код для генерації цих матриць і тут, щоб показати, що він не довший, ніж інші методи, запропоновані тут. Будь ласка, дивіться мою пов'язану відповідь для деяких пояснень. Значення betaparamдля наведеної вище цифри були (а розмірність - 100 ).50,20,10,5,2,1d100

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

Оновлення: власні значення

@psarka запитує про власні значення цих матриць. На малюнку нижче я будую спектри власних значень тих самих шести кореляційних матриць, як вище. Зауважте, що вони зменшуються поступово; на відміну від цього, запропонований @psarka метод, як правило, призводить до кореляційної матриці з одним великим власним значенням, але решта є досить рівномірною.

власні значення матриць вище


Оновлення. Дійсно простий метод: кілька факторів

k<nWk×nWWDB=WW+Dk=100,50,20,10,5,1

випадкові кореляційні матриці від випадкових факторів

k

власних спектрів цих матриць

Ось код:

d = 100;    %// number of dimensions
k = 5;      %// number of factors

W = randn(d,k);
S = W*W' + diag(rand(1,d));
S = diag(1./sqrt(diag(S))) * S * diag(1./sqrt(diag(S)));

+1. Однак ось лише нагадування до вашого останнього розділу про "факторний метод". Строго правильний підхід називає, що стовпці Wортогональні (тобто косинуси між ними 0). Просто генерування випадкових Wвипадків, звичайно, не забезпечує цього. Якщо вони не є ортогональними - тобто фактори є похилими (виклик , то в Wякості W_) - фактор теорема НЕ WW'тільки W_CW_'з Cбути «кореляція» (косинуси) між факторами. Тепер, C=Q'Qз Qбудучи неортогональної матрицю обертання обертання W_=inv(Q)'W(і так W=W_Q'). Створити деякі Q- матрицю зі стовпцем ss = 1 та матрицю ss = розмір матриці.
ttnphns

... помилка: W_=inv(Q)'Wзвичайно W_= W inv(Q)'.
ttnphns

WWW+DW

1
Переклад цього на R:W = replicate(k, rnorm(d)); S = W%*%t(W) + diag(rnorm(d),nrow=d); S = diag(1/sqrt(diag(S)))%*%S%*%diag(1/sqrt(diag(S)))
Скотт Уорланд

1
@Mihai, вдалий пункт та ваші пропозиції, ймовірно, найпростіші. Ви також можете зробитиS <- matrix(nearPD(S, corr = TRUE, keepDiag = TRUE)$mat@x,ncol(S),ncol(S))
Скотт Уорланд

7

a

import numpy as np
from random import choice
import matplotlib.pyplot as plt

n = 100
a = 2

A = np.matrix([np.random.randn(n) + np.random.randn(1)*a for i in range(n)])
A = A*np.transpose(A)
D_half = np.diag(np.diag(A)**(-0.5))
C = D_half*A*D_half

vals = list(np.array(C.ravel())[0])
plt.hist(vals, range=(-1,1))
plt.show()
plt.imshow(C, interpolation=None)
plt.show()

Дещо рівномірний розподіл Результати імшоу


crsk[a,a]X

Так, ви абсолютно праві! (О, хлопче, це було справді нерозумно: D). Я змінив випадкову частину на randn (1) * a і тепер це набагато краще.
псарка

k

an

Недоліком цього методу є те, що отримана кореляційна матриця має одне велике власне значення, але решта майже однакові. Тож ця процедура не дає "загальної" кореляційної матриці ... Не те, що я вказав у своєму питанні. Але @ssdecontrol в коментарях вище згадував, що, мабуть, є способи вибірки з усіх кореляційних матриць; це виглядає цікаво, але набагато складніше.
Амеба каже: Відновити Моніку

6

Хм, після того, як я зробив приклад на своїй мові MatMate, я бачу, що вже є відповідь python, що може бути кращим, оскільки python широко використовується. Але оскільки у вас були ще запитання, я показую вам мій підхід із використанням мови Matmate-matrix, можливо, це більше самозакоментування.

Спосіб 1
(Використання MatMate):

v=12         // 12 variables
f=3          // subset-correlation based on 3 common factors
vg = v / f   // variables per subsets

 // generate hidden factor-matrix
             // randomu(rows,cols ,lowbound, ubound) gives uniform random matrix 
             //    without explicite bounds the default is: randomu(rows,cols,0,100)
L = {   randomu(vg,f)     || randomu(vg,f)/100  || randomu(vg,f)/100 , _
        randomu(vg,f)/100 || randomu(vg,f)      || randomu(vg,f)/100 , _
        randomu(vg,f)/100 || randomu(vg,f)/100  || randomu(vg,f)     }

 // make sure there is itemspecific variance
 // by appending a diagonal-matrix with random positive entries
L = L || mkdiag(randomu(v,1,10,20)) 
  // make covariance and correlation matrix
cov = L *'   // L multiplied  with its transpose
cor = covtocorr(cov)
                   set ccdezweite=3 ccfeldweite=8
                   list cor
cor = 
   1.000,   0.321,   0.919,   0.489,   0.025,   0.019,   0.019,   0.030,   0.025,   0.017,   0.014,   0.014
   0.321,   1.000,   0.540,   0.923,   0.016,   0.015,   0.012,   0.030,   0.033,   0.016,   0.012,   0.015
   0.919,   0.540,   1.000,   0.679,   0.018,   0.014,   0.012,   0.029,   0.028,   0.014,   0.012,   0.012
   0.489,   0.923,   0.679,   1.000,   0.025,   0.022,   0.020,   0.040,   0.031,   0.014,   0.011,   0.014
   0.025,   0.016,   0.018,   0.025,   1.000,   0.815,   0.909,   0.758,   0.038,   0.012,   0.018,   0.014
   0.019,   0.015,   0.014,   0.022,   0.815,   1.000,   0.943,   0.884,   0.035,   0.012,   0.014,   0.012
   0.019,   0.012,   0.012,   0.020,   0.909,   0.943,   1.000,   0.831,   0.036,   0.013,   0.015,   0.010
   0.030,   0.030,   0.029,   0.040,   0.758,   0.884,   0.831,   1.000,   0.041,   0.017,   0.022,   0.020
   0.025,   0.033,   0.028,   0.031,   0.038,   0.035,   0.036,   0.041,   1.000,   0.831,   0.868,   0.780
   0.017,   0.016,   0.014,   0.014,   0.012,   0.012,   0.013,   0.017,   0.831,   1.000,   0.876,   0.848
   0.014,   0.012,   0.012,   0.011,   0.018,   0.014,   0.015,   0.022,   0.868,   0.876,   1.000,   0.904
   0.014,   0.015,   0.012,   0.014,   0.014,   0.012,   0.010,   0.020,   0.780,   0.848,   0.904,   1.000

Проблема тут може полягати в тому, що ми визначаємо блоки субматриць, які мають високі кореляції всередині з невеликою кореляцією між, і це не програмно, а постійні вирази конкатенації. Можливо, цей підхід можна було б моделювати більш елегантно в python.


Метод 2 (a)
Після цього існує зовсім інший підхід, коли ми заповнюємо можливу залишилася коваріацію випадковими сумами в 100 відсотків у матрицю множин факторів. Це робиться в Pari / GP:

{L = matrix(8,8);  \\ generate an empty factor-loadings-matrix
for(r=1,8, 
   rv=1.0;    \\ remaining variance for variable is 1.0
   for(c=1,8,
        pv=if(c<8,random(100)/100.0,1.0); \\ define randomly part of remaining variance
        cv= pv * rv;  \\ compute current partial variance
        rv = rv - cv;     \\ compute the now remaining variance
        sg = (-1)^(random(100) % 2) ;  \\ also introduce randomly +- signs
        L[r,c] = sg*sqrt(cv) ;  \\ compute factor loading as signed sqrt of cv
       )
     );}

cor = L * L~

і отримана кореляційна матриця є

     1.000  -0.7111  -0.08648   -0.7806   0.8394  -0.7674   0.6812    0.2765
   -0.7111    1.000   0.06073    0.7485  -0.7550   0.8052  -0.8273   0.05863
  -0.08648  0.06073     1.000    0.5146  -0.1614   0.1459  -0.4760  -0.01800
   -0.7806   0.7485    0.5146     1.000  -0.8274   0.7644  -0.9373  -0.06388
    0.8394  -0.7550   -0.1614   -0.8274    1.000  -0.5823   0.8065   -0.1929
   -0.7674   0.8052    0.1459    0.7644  -0.5823    1.000  -0.7261   -0.4822
    0.6812  -0.8273   -0.4760   -0.9373   0.8065  -0.7261    1.000   -0.1526
    0.2765  0.05863  -0.01800  -0.06388  -0.1929  -0.4822  -0.1526     1.000

Можливо, це генерує кореляційну матрицю з домінуючими основними компонентами через кумулятивного правила генерування для матриці факторних навантажень. Також може бути краще забезпечити позитивну точність, зробивши останню порцію дисперсії унікальним фактором. Я залишив це в програмі, щоб зберегти фокус на загальному принципі.

Кореляційна матриця розміром 100x100 мала такі частоти кореляцій (округлені до 1 декаду)

    e    f            e: entry(rounded) f: frequency
  -----------------------------------------------------
  -1.000, 108.000
  -0.900, 460.000
  -0.800, 582.000
  -0.700, 604.000
  -0.600, 548.000
  -0.500, 540.000
  -0.400, 506.000
  -0.300, 482.000
  -0.200, 488.000
  -0.100, 464.000
   0.000, 434.000
   0.100, 486.000
   0.200, 454.000
   0.300, 468.000
   0.400, 462.000
   0.500, 618.000
   0.600, 556.000
   0.700, 586.000
   0.800, 536.000
   0.900, 420.000
   1.000, 198.000

[оновлення]. Гм, матриця 100х100 погано обумовлена; Pari / GP не може правильно визначити власні значення за допомогою напівротів (charpoly ()) - функціонує навіть з точністю до 200 цифр. Я здійснив обертання якобі до pca-форми на завантажувальній матриці L і знайшов переважно надзвичайно малі власні значення, надрукував їх у логарифмах до основи 10 (які приблизно дають позицію десяткової крапки). Читайте зліва направо, а потім рядок за рядком:

log_10(eigenvalues):
   1.684,   1.444,   1.029,   0.818,   0.455,   0.241,   0.117,  -0.423,  -0.664,  -1.040
  -1.647,  -1.799,  -1.959,  -2.298,  -2.729,  -3.059,  -3.497,  -3.833,  -4.014,  -4.467
  -4.992,  -5.396,  -5.511,  -6.366,  -6.615,  -6.834,  -7.535,  -8.138,  -8.263,  -8.766
  -9.082,  -9.482,  -9.940, -10.167, -10.566, -11.110, -11.434, -11.788, -12.079, -12.722
 -13.122, -13.322, -13.444, -13.933, -14.390, -14.614, -15.070, -15.334, -15.904, -16.278
 -16.396, -16.708, -17.022, -17.746, -18.090, -18.358, -18.617, -18.903, -19.186, -19.476
 -19.661, -19.764, -20.342, -20.648, -20.805, -20.922, -21.394, -21.740, -21.991, -22.291
 -22.792, -23.184, -23.680, -24.100, -24.222, -24.631, -24.979, -25.161, -25.282, -26.211
 -27.181, -27.626, -27.861, -28.054, -28.266, -28.369, -29.074, -29.329, -29.539, -29.689
 -30.216, -30.784, -31.269, -31.760, -32.218, -32.446, -32.785, -33.003, -33.448, -34.318

[оновлення 2]
Метод 2 (b)
Можливим вдосконаленням може бути збільшення специфічної відміни пункту до деякого не граничного рівня та зменшення до порівняно меншої кількості загальних факторів (наприклад, цілочисельне-квадратне коріння числа):

{  dimr = 100;
   dimc = sqrtint(dimr);        \\ 10 common factors
   L = matrix(dimr,dimr+dimc);  \\ loadings matrix 
                                \\     with dimr itemspecific and 
                                \\          dimc common factors
   for(r=1,dim, 
         vr=1.0;                \\ complete variance per item 
         vu=0.05+random(100)/1000.0;   \\ random variance +0.05
                                       \\ for itemspecific variance
         L[r,r]=sqrt(vu);              \\ itemspecific factor loading  
         vr=vr-vu;
         for(c=1,dimc,
                cv=if(c<dimc,random(100)/100,1.0)*vr;
                vr=vr-cv;
                L[r,dimr+c]=(-1)^(random(100) % 2)*sqrt(cv)
             )
        );}

   cov=L*L~
   cp=charpoly(cov)   \\ does not work even with 200 digits precision
   pr=polroots(cp)    \\ spurious negative and complex eigenvalues...

Структура результату

в терміні розподілу кореляцій:зображення

залишається подібним (також неприємна нерозкладаність від PariGP), але власні значення, знайдені джакобі-обертанням завантажувальної матриці, тепер мають кращу структуру, для нещодавно обчисленого прикладу я отримав власні значення як

log_10(eigenvalues):
   1.677,   1.326,   1.063,   0.754,   0.415,   0.116,  -0.262,  -0.516,  -0.587,  -0.783
  -0.835,  -0.844,  -0.851,  -0.854,  -0.858,  -0.862,  -0.862,  -0.868,  -0.872,  -0.873
  -0.878,  -0.882,  -0.884,  -0.890,  -0.895,  -0.896,  -0.896,  -0.898,  -0.902,  -0.904
  -0.904,  -0.909,  -0.911,  -0.914,  -0.920,  -0.923,  -0.925,  -0.927,  -0.931,  -0.935
  -0.939,  -0.939,  -0.943,  -0.948,  -0.951,  -0.955,  -0.956,  -0.960,  -0.967,  -0.969
  -0.973,  -0.981,  -0.986,  -0.989,  -0.997,  -1.003,  -1.005,  -1.011,  -1.014,  -1.019
  -1.022,  -1.024,  -1.031,  -1.038,  -1.040,  -1.048,  -1.051,  -1.061,  -1.064,  -1.068
  -1.070,  -1.074,  -1.092,  -1.092,  -1.108,  -1.113,  -1.120,  -1.134,  -1.139,  -1.147
  -1.150,  -1.155,  -1.158,  -1.166,  -1.171,  -1.175,  -1.184,  -1.184,  -1.192,  -1.196
  -1.200,  -1.220,  -1.237,  -1.245,  -1.252,  -1.262,  -1.269,  -1.282,  -1.287,  -1.290

Дуже дякую! Дуже цікаво, але мені знадобиться певний час, щоб переварити ...
Амеба каже "Відновити Моніку"

Мені все-таки належить уважно пройти вашу відповідь, але тим часом я прочитав статтю про вибірку матриць випадкових кореляцій, і один із методів звідти можна використати, щоб зробити саме те, що мені потрібно. Я опублікував відповідь тут, можливо, вам буде цікаво поглянути! Він посилається на набагато детальнішу відповідь, яку я написав в іншій темі.
Амеба каже: Відновити Моніку

@amoeba: щасливий, що ти знайшов щось приємне для тебе! Це цікаве питання, я повернусь пізніше до цього самого, можливо, вдосконалюю / адаптую MatMate-процедури (і зроблю їх підпрограми) відповідно до документа, над яким ви працювали.
Готфрід Гельмс

2

ABλA+(1λ)Bλ

ABCλAA+λBB+λCCλ=1λ0


AB

Так, але з такого алгоритму та відповідної різноманітності у "вершинах" (тобто матрицях), які визначають ваш політоп позитивно-визначених кореляційних матриць, ви можете використовувати вибірку відхилення, щоб отримати будь-який розподіл власних значень, рівномірність записів, тощо, що ви хочете. Однак мені незрозуміло, якою була б хороша основа. Звучить як питання для того, хто вивчав абстрактну алгебру останнім часом, ніж я.
Ендрю М

Привіт ще раз, я прочитав статтю про вибірку матриць випадкової кореляції, і один із методів звідти можна використати, щоб зробити саме те, що мені потрібно. Я опублікував відповідь тут, можливо, вам буде цікаво поглянути! Він посилається на набагато детальнішу відповідь, яку я написав в іншій темі.
Амеба повідомляє, що повернеться Моніка

2

R має пакет (clusterGeneration), який реалізує метод у:

Приклад:

> (cormat10 = clusterGeneration::rcorrmatrix(10, alphad = 1/100000000000000))
        [,1]   [,2]    [,3]     [,4]     [,5]   [,6]   [,7]    [,8]     [,9]   [,10]
 [1,]  1.000  0.344 -0.1406 -0.65786 -0.19411  0.246  0.688 -0.6146  0.36971 -0.1052
 [2,]  0.344  1.000 -0.4256 -0.35512  0.15973  0.192  0.340 -0.4907 -0.30539 -0.6104
 [3,] -0.141 -0.426  1.0000  0.01775 -0.61507 -0.485 -0.273  0.3492 -0.30284  0.1647
 [4,] -0.658 -0.355  0.0178  1.00000  0.00528 -0.335 -0.124  0.5256 -0.00583 -0.0737
 [5,] -0.194  0.160 -0.6151  0.00528  1.00000  0.273 -0.350 -0.0785  0.08285  0.0985
 [6,]  0.246  0.192 -0.4847 -0.33531  0.27342  1.000  0.278 -0.2220 -0.11010  0.0720
 [7,]  0.688  0.340 -0.2734 -0.12363 -0.34972  0.278  1.000 -0.6409  0.40314 -0.2800
 [8,] -0.615 -0.491  0.3492  0.52557 -0.07852 -0.222 -0.641  1.0000 -0.50796  0.1461
 [9,]  0.370 -0.305 -0.3028 -0.00583  0.08285 -0.110  0.403 -0.5080  1.00000  0.3219
[10,] -0.105 -0.610  0.1647 -0.07373  0.09847  0.072 -0.280  0.1461  0.32185  1.0000
> cormat10[lower.tri(cormat10)] %>% psych::describe()
   vars  n  mean   sd median trimmed mad   min  max range skew kurtosis   se
X1    1 45 -0.07 0.35  -0.08   -0.07 0.4 -0.66 0.69  1.35 0.03       -1 0.05

На жаль, не представляється можливим моделювати кореляції, що слідують за рівномірним розподілом результатів. Здається, зробити сильніші кореляції, коли alphadвстановлено дуже малі значення, але навіть при 1/100000000000000цьому діапазон кореляцій підніметься лише до приблизно 1,40.

Тим не менш, я сподіваюся, що це комусь може принести користь.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.