J'ai deux tableaux qui ont les formes N X T
et M X T
. Je voudrais calculer le coefficient de corrélation à travers T
entre chaque paire de lignes possible n
et m
(à partir de N
et M
, respectivement).
Quelle est la manière la plus rapide et la plus Pythonique de le faire? (Il me semble que boucler sur N
et M
n'est ni rapide ni Pythonic.) Je m'attends à ce que la réponse implique numpy
et/ou scipy
. À l'heure actuelle, mes tableaux sont numpy
array
s, mais je suis prêt à les convertir en un autre type.
Je m'attends à ce que ma sortie soit un tableau de la forme N X M
.
N.B. Quand je dis "coefficient de corrélation", je veux dire le coefficient de corrélation produit-moment de Pearson .
Voici quelques éléments à noter:
numpy
correlate
requiert que les tableaux d'entrée soient unidimensionnels.numpy
corrcoef
accepte les tableaux bidimensionnels, mais ils doivent avoir la même forme.scipy.stats
fonction pearsonr
requiert que les tableaux d'entrée soient unidimensionnels.Corrélation (cas "valide" par défaut) entre deux tableaux 2D:
Vous pouvez simplement utiliser la multiplication matricielle np.dot
comme ça -
out = np.dot(arr_one,arr_two.T)
La corrélation avec le cas par défaut "valid"
Entre chaque combinaison de lignes par paire (ligne1, ligne2) des deux tableaux d'entrée correspondrait au résultat de la multiplication à chaque position (ligne1, ligne2).
Calcul du coefficient de corrélation par ligne pour deux tableaux 2D:
def corr2_coeff(A,B):
# Rowwise mean of input arrays & subtract from input arrays themeselves
A_mA = A - A.mean(1)[:,None]
B_mB = B - B.mean(1)[:,None]
# Sum of squares across rows
ssA = (A_mA**2).sum(1);
ssB = (B_mB**2).sum(1);
# Finally get corr coeff
return np.dot(A_mA,B_mB.T)/np.sqrt(np.dot(ssA[:,None],ssB[None]))
Ceci est basé sur cette solution pour How to apply corr2 functions in Multidimentional arrays in MATLAB
Analyse comparative
Cette section compare les performances d'exécution avec l'approche proposée par rapport à l'approche basée sur generate_correlation_map
& Loopy pearsonr
répertoriée dans autre réponse. (tirée de la fonction test_generate_correlation_map()
sans le code de vérification de l'exactitude de la valeur à la fin). Veuillez noter que les délais pour l'approche proposée incluent également une vérification au début pour vérifier le nombre égal de colonnes dans les deux tableaux d'entrée, comme cela est également fait dans cette autre réponse. Les temps d'exécution sont répertoriés ci-dessous.
Cas 1:
In [106]: A = np.random.Rand(1000,100)
In [107]: B = np.random.Rand(1000,100)
In [108]: %timeit corr2_coeff(A,B)
100 loops, best of 3: 15 ms per loop
In [109]: %timeit generate_correlation_map(A, B)
100 loops, best of 3: 19.6 ms per loop
Cas n ° 2:
In [110]: A = np.random.Rand(5000,100)
In [111]: B = np.random.Rand(5000,100)
In [112]: %timeit corr2_coeff(A,B)
1 loops, best of 3: 368 ms per loop
In [113]: %timeit generate_correlation_map(A, B)
1 loops, best of 3: 493 ms per loop
Cas n ° 3:
In [114]: A = np.random.Rand(10000,10)
In [115]: B = np.random.Rand(10000,10)
In [116]: %timeit corr2_coeff(A,B)
1 loops, best of 3: 1.29 s per loop
In [117]: %timeit generate_correlation_map(A, B)
1 loops, best of 3: 1.83 s per loop
L'autre approche en boucle pearsonr based
Semblait trop lente, mais voici les temps d'exécution pour une petite taille de données -
In [118]: A = np.random.Rand(1000,100)
In [119]: B = np.random.Rand(1000,100)
In [120]: %timeit corr2_coeff(A,B)
100 loops, best of 3: 15.3 ms per loop
In [121]: %timeit generate_correlation_map(A, B)
100 loops, best of 3: 19.7 ms per loop
In [122]: %timeit pearsonr_based(A,B)
1 loops, best of 3: 33 s per loop
@Divakar fournit une excellente option pour calculer la corrélation non mise à l'échelle, ce que j'ai initialement demandé.
Pour calculer le coefficient de corrélation, un peu plus est nécessaire:
import numpy as np
def generate_correlation_map(x, y):
"""Correlate each n with each m.
Parameters
----------
x : np.array
Shape N X T.
y : np.array
Shape M X T.
Returns
-------
np.array
N X M array in which each element is a correlation coefficient.
"""
mu_x = x.mean(1)
mu_y = y.mean(1)
n = x.shape[1]
if n != y.shape[1]:
raise ValueError('x and y must ' +
'have the same number of timepoints.')
s_x = x.std(1, ddof=n - 1)
s_y = y.std(1, ddof=n - 1)
cov = np.dot(x,
y.T) - n * np.dot(mu_x[:, np.newaxis],
mu_y[np.newaxis, :])
return cov / np.dot(s_x[:, np.newaxis], s_y[np.newaxis, :])
Voici un test de cette fonction, qui réussit:
from scipy.stats import pearsonr
def test_generate_correlation_map():
x = np.random.Rand(10, 10)
y = np.random.Rand(20, 10)
desired = np.empty((10, 20))
for n in range(x.shape[0]):
for m in range(y.shape[0]):
desired[n, m] = pearsonr(x[n, :], y[m, :])[0]
actual = generate_correlation_map(x, y)
np.testing.assert_array_almost_equal(actual, desired)