web-dev-qa-db-fra.com

Coefficients de corrélation et valeurs p pour toutes les paires de lignes d'une matrice

J'ai une matrice data avec m rangées et n colonnes. J'avais l'habitude de calculer les coefficients de corrélation entre toutes les paires de lignes en utilisant np.corrcoef :

import numpy as np
data = np.array([[0, 1, -1], [0, -1, 1]])
np.corrcoef(data)

Maintenant, j'aimerais aussi examiner les valeurs p de ces coefficients. np.corrcoef ne les fournit pas; scipy.stats.pearsonr does. Cependant, scipy.stats.pearsonr n'accepte pas de matrice en entrée. 

Existe-t-il un moyen rapide de calculer à la fois le coefficient et la valeur p pour toutes les paires de lignes (en arrivant par exemple à deux matrices m par m /, l'une avec des coefficients de corrélation, l'autre avec les valeurs p correspondantes) sans avoir à parcourir manuellement toutes les paires?

16
John Manak

J'ai rencontré le même problème aujourd'hui.

Après une demi-heure de recherches sur Google, je ne trouve aucun code dans la bibliothèque numpy/scipy qui puisse m'aider. 

J'ai donc écrit ma propre version de corrcoef

import numpy as np
from scipy.stats import pearsonr, betai

def corrcoef(matrix):
    r = np.corrcoef(matrix)
    rf = r[np.triu_indices(r.shape[0], 1)]
    df = matrix.shape[1] - 2
    ts = rf * rf * (df / (1 - rf * rf))
    pf = betai(0.5 * df, 0.5, df / (df + ts))
    p = np.zeros(shape=r.shape)
    p[np.triu_indices(p.shape[0], 1)] = pf
    p[np.tril_indices(p.shape[0], -1)] = pf
    p[np.diag_indices(p.shape[0])] = np.ones(p.shape[0])
    return r, p

def corrcoef_loop(matrix):
    rows, cols = matrix.shape[0], matrix.shape[1]
    r = np.ones(shape=(rows, rows))
    p = np.ones(shape=(rows, rows))
    for i in range(rows):
        for j in range(i+1, rows):
            r_, p_ = pearsonr(matrix[i], matrix[j])
            r[i, j] = r[j, i] = r_
            p[i, j] = p[j, i] = p_
    return r, p

La première version utilise le résultat de np.corrcoef, puis calcule la valeur p basée sur les valeurs triangulaires supérieures de la matrice corrcoef.

La seconde version de la boucle, qui se contente de parcourir les rangées, est créée manuellement.

def test_corrcoef():
    a = np.array([
        [1, 2, 3, 4],
        [1, 3, 1, 4],
        [8, 3, 8, 5]])

    r1, p1 = corrcoef(a)
    r2, p2 = corrcoef_loop(a)

    assert np.allclose(r1, r2)
    assert np.allclose(p1, p2)

Le test réussi, ils sont les mêmes.

def test_timing():
    import time
    a = np.random.randn(100, 2500)

    def timing(func, *args, **kwargs):
        t0 = time.time()
        loops = 10
        for _ in range(loops):
            func(*args, **kwargs)
        print('{} takes {} seconds loops={}'.format(
            func.__name__, time.time() - t0, loops))

    timing(corrcoef, a)
    timing(corrcoef_loop, a)


if __== '__main__':
    test_corrcoef()
    test_timing()

La performance sur mon Macbook contre la matrice 100x2500

corrcoef prend 0.06608104705810547 secondes en boucle = 10

corrcoef_loop prend 7.585600137710571 secondes en boucle = 10

12
jingchao

Le moyen le plus judicieux de le faire pourrait être la méthode de construction .corr dans pandas, pour obtenir r:

In [79]:

import pandas as pd
m=np.random.random((6,6))
df=pd.DataFrame(m)
print df.corr()
          0         1         2         3         4         5
0  1.000000 -0.282780  0.455210 -0.377936 -0.850840  0.190545
1 -0.282780  1.000000 -0.747979 -0.461637  0.270770  0.008815
2  0.455210 -0.747979  1.000000 -0.137078 -0.683991  0.557390
3 -0.377936 -0.461637 -0.137078  1.000000  0.511070 -0.801614
4 -0.850840  0.270770 -0.683991  0.511070  1.000000 -0.499247
5  0.190545  0.008815  0.557390 -0.801614 -0.499247  1.000000

Pour obtenir les valeurs p en utilisant le test t:

In [84]:

n=6
r=df.corr()
t=r*np.sqrt((n-2)/(1-r*r))

import scipy.stats as ss
ss.t.cdf(t, n-2)
Out[84]:
array([[ 1.        ,  0.2935682 ,  0.817826  ,  0.23004382,  0.01585695,
         0.64117917],
       [ 0.2935682 ,  1.        ,  0.04363408,  0.17836685,  0.69811422,
         0.50661121],
       [ 0.817826  ,  0.04363408,  1.        ,  0.39783538,  0.06700715,
         0.8747497 ],
       [ 0.23004382,  0.17836685,  0.39783538,  1.        ,  0.84993082,
         0.02756579],
       [ 0.01585695,  0.69811422,  0.06700715,  0.84993082,  1.        ,
         0.15667393],
       [ 0.64117917,  0.50661121,  0.8747497 ,  0.02756579,  0.15667393,
         1.        ]])
In [85]:

ss.pearsonr(m[:,0], m[:,1])
Out[85]:
(-0.28277983892175751, 0.58713640696703184)
In [86]:
#be careful about the difference of 1-tail test and 2-tail test:
0.58713640696703184/2
Out[86]:
0.2935682034835159 #the value in ss.t.cdf(t, n-2) [0,1] cell

Aussi, vous pouvez simplement utiliser le scipy.stats.pearsonr que vous avez mentionné dans OP:

In [95]:
#returns a list of tuples of (r, p, index1, index2)
import itertools
[ss.pearsonr(m[:,i],m[:,j])+(i, j) for i, j in itertools.product(range(n), range(n))]
Out[95]:
[(1.0, 0.0, 0, 0),
 (-0.28277983892175751, 0.58713640696703184, 0, 1),
 (0.45521036266021014, 0.36434799921123057, 0, 2),
 (-0.3779357902414715, 0.46008763115463419, 0, 3),
 (-0.85083961671703368, 0.031713908656676448, 0, 4),
 (0.19054495489542525, 0.71764166168348287, 0, 5),
 (-0.28277983892175751, 0.58713640696703184, 1, 0),
 (1.0, 0.0, 1, 1),
#etc, etc
9
CT Zhu

Sorte de hackish et peut-être inefficace, mais je pense que cela pourrait être ce que vous cherchez:

import scipy.spatial.distance as dist

import scipy.stats as ss

# Pearson's correlation coefficients
print dist.squareform(dist.pdist(data, lambda x, y: ss.pearsonr(x, y)[0]))    

# p-values
print dist.squareform(dist.pdist(data, lambda x, y: ss.pearsonr(x, y)[1]))

La fonction pdist de Scipy est une fonction très utile, principalement destinée à rechercher les distances par paires entre les observations dans un espace à n dimensions. 

Mais il permet à des «métriques de distance» appelables définies par l'utilisateur, qui peuvent être exploitées pour effectuer tout type d'opération par paire. Le résultat est renvoyé sous forme de matrice de distance condensée, qui peut facilement être modifiée en matrice carrée à l’aide de la fonction 'squareform' de Scipy .

4
Ketan

Si vous ne devez pas utiliser coefficient de corrélation pearson , vous pouvez utiliser le coefficient de corrélation spearman , car il renvoie à la fois la matrice de corrélation et les valeurs p (notez que le premier requiert que votre les données sont normalement distribuées, alors que la corrélation de Spearman est une mesure non paramétrique, ne supposant donc pas la distribution normale de vos données). Un exemple de code:

from scipy import stats
import numpy as np

data = np.array([[0, 1, -1], [0, -1, 1], [0, 1, -1]])
print 'np.corrcoef:', np.corrcoef(data)
cor, pval = stats.spearmanr(data.T)
print 'stats.spearmanr - cor:\n', cor
print 'stats.spearmanr - pval\n', pval
1
Sahar