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?
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
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
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 .
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