2012-02-13 13 views
10

Tengo dos matrices muy grandes (60x25000) y me gustaría calcular la correlación entre las columnas solo entre las dos matrices. Por ejemplo:¿Cuál es una forma rápida de calcular la correlación columna por columna en matlab

corrVal(1) = corr(mat1(:,1), mat2(:,1); 
corrVal(2) = corr(mat1(:,2), mat2(:,2); 
... 
corrVal(i) = corr(mat1(:,i), mat2(:,i); 

Para matrices más pequeñas que simplemente puede utilizar:

colCorr = diag(corr(mat1, mat2)); 

pero esto no funciona para matrices muy grandes que me quede sin memoria. Consideré dividir las matrices para calcular las correlaciones y luego combinar los resultados, pero me parece un desperdicio calcular la correlación entre las combinaciones de columnas que realmente no me interesan.

¿Existe una manera rápida de calcular directamente lo que me interesa?

Editar: He usado un bucle en el pasado, pero es sólo a modo lento:

mat1 = rand(60,5000); 
mat2 = rand(60,5000); 
nCol = size(mat1,2); 
corrVal = zeros(nCol,1); 

tic; 
for i = 1:nCol 
    corrVal(i) = corr(mat1(:,i), mat2(:,i)); 
end 
toc; 

Esta toma ~ 1 segundo

tic; 
corrVal = diag(corr(mat1,mat2)); 
toc; 

Esto tarda unos 0,2 segundos

+0

He hecho una edición de su publicación; por favor revisa si es correcto. – Jacob

+1

Además, ¿qué pasa con lo obvio para el ciclo? – Jacob

+0

la edición es correcta, ¡gracias! También el ciclo es forma de ralentizar – slayton

Respuesta

15

puedo obtener una mejora x100 velocidad calculando a mano.

An=bsxfun(@minus,A,mean(A,1)); %%% zero-mean 
Bn=bsxfun(@minus,B,mean(B,1)); %%% zero-mean 
An=bsxfun(@times,An,1./sqrt(sum(An.^2,1))); %% L2-normalization 
Bn=bsxfun(@times,Bn,1./sqrt(sum(Bn.^2,1))); %% L2-normalization 
C=sum(An.*Bn,1); %% correlation 

Puede comparar el uso de ese código:

A=rand(60,25000); 
B=rand(60,25000); 

tic; 
C=zeros(1,size(A,2)); 
for i = 1:size(A,2) 
    C(i)=corr(A(:,i), B(:,i)); 
end 
toc; 

tic 
An=bsxfun(@minus,A,mean(A,1)); 
Bn=bsxfun(@minus,B,mean(B,1)); 
An=bsxfun(@times,An,1./sqrt(sum(An.^2,1))); 
Bn=bsxfun(@times,Bn,1./sqrt(sum(Bn.^2,1))); 
C2=sum(An.*Bn,1); 
toc 
mean(abs(C-C2)) %% difference between methods 

Éstos son los tiempos de cálculo:

Elapsed time is 10.822766 seconds. 
Elapsed time is 0.119731 seconds. 

La diferencia entre los dos resultados es muy pequeña:

mean(abs(C-C2)) 

ans = 
    3.0968e-17 

EDIT: explicación

bsxfun realiza una operación columna por columna (o fila por fila dependiendo de la entrada).

An=bsxfun(@minus,A,mean(A,1)); 

Esta línea se eliminará (@minus) la media de cada columna (mean(A,1)) a cada columna de A ... Así que básicamente hace que las columnas de A de media cero.

An=bsxfun(@times,An,1./sqrt(sum(An.^2,1))); 

Esta línea multiplica (@times) cada columna por el inverso de su norma. Entonces los hace L-2 normalizado.

Una vez que las columnas son de media cero y L2-normalizadas, para calcular la correlación, solo tiene que hacer el producto de puntos de cada columna de An con cada columna de B.Entonces, multiplíquelos por elemento An.*Bn, y luego sume cada columna: sum(An.*Bn);.

+0

wow eso es rápido. ¿Me puede dar una explicación rápida de por qué esto funciona? – slayton

+1

He agregado algunas explicaciones. Espero que no quede claro ... – Oli

+0

@Oli: ¡Excelente respuesta! – Jacob

1

Creo que el ciclo obvio podría ser lo suficientemente bueno para su tamaño de problema. En mi portátil se tarda menos de 6 segundos para hacer lo siguiente:

A = rand(60,25000); 
B = rand(60,25000); 
n = size(A,1); 
m = size(A,2); 

corrVal = zeros(1,m); 
for k=1:m 
    corrVal(k) = corr(A(:,k),B(:,k)); 
end 
+0

whoops, no vi tu edición. Wow, diag es mucho más rápido. –

+0

Espera, ¿qué me estoy perdiendo? diag (corr (A, B)); toma más de 10 segundos para mí. –

+0

Sí. Igual que aquí. – Jacob

Cuestiones relacionadas