web-dev-qa-db-fra.com

Calcul du coefficient de corrélation entre deux tableaux multidimensionnels

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 numpyarrays, 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:

  • La fonction numpycorrelate requiert que les tableaux d'entrée soient unidimensionnels.
  • La fonction numpycorrcoef accepte les tableaux bidimensionnels, mais ils doivent avoir la même forme.
  • Le scipy.stats fonction pearsonr requiert que les tableaux d'entrée soient unidimensionnels.
21
dbliss

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
29
Divakar

@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)
6
dbliss