valor - ¿Cuál es una forma rápida de calcular la correlación columna por columna en matlab
seleccionar elementos de una matriz matlab (2)
Tengo dos matrices muy grandes (60x25000) y me gustaría calcular la correlación entre las columnas solo entre las dos matrices. Por ejemplo:
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, simplemente puedo usar:
colCorr = diag( corr( mat1, mat2 ) );
pero esto no funciona para matrices muy grandes ya que me quedo 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.
¿Hay una forma rápida de calcular directamente lo que me interesa?
Editar : he usado un ciclo en el pasado pero es solo una forma de ralentizarlo:
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;
Esto lleva ~ 1 segundo
tic;
corrVal = diag(corr(mat1,mat2));
toc;
Esto lleva ~ 0.2 segundos
Creo que el bucle obvio podría ser lo suficientemente bueno para su tamaño de problema. En mi computadora portátil, toma menos de 6 segundos 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
Puedo obtener una mejora de velocidad x100 informándola 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
Puedes comparar usando 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
Aquí están los tiempos de computación:
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
EDITAR: 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 eliminará ( @minus
) la media de cada columna ( mean(A,1)
) para cada columna de A
... Así que básicamente hace que las columnas de A
sean 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-normalizada, para calcular la correlación, solo tiene que hacer el producto de puntos de cada columna de An
con cada columna de B
Entonces los multiplicas por elementos An.*Bn
, y luego sumas cada columna: sum(An.*Bn);
.