Calcul de la corrélation et de la signification de Pearson en Python

Réponses:

202

Vous pouvez voir scipy.stats:

from pydoc import help
from scipy.stats.stats import pearsonr
help(pearsonr)

>>>
Help on function pearsonr in module scipy.stats.stats:

pearsonr(x, y)
 Calculates a Pearson correlation coefficient and the p-value for testing
 non-correlation.

 The Pearson correlation coefficient measures the linear relationship
 between two datasets. Strictly speaking, Pearson's correlation requires
 that each dataset be normally distributed. Like other correlation
 coefficients, this one varies between -1 and +1 with 0 implying no
 correlation. Correlations of -1 or +1 imply an exact linear
 relationship. Positive correlations imply that as x increases, so does
 y. Negative correlations imply that as x increases, y decreases.

 The p-value roughly indicates the probability of an uncorrelated system
 producing datasets that have a Pearson correlation at least as extreme
 as the one computed from these datasets. The p-values are not entirely
 reliable but are probably reasonable for datasets larger than 500 or so.

 Parameters
 ----------
 x : 1D array
 y : 1D array the same length as x

 Returns
 -------
 (Pearson's correlation coefficient,
  2-tailed p-value)

 References
 ----------
 http://www.statsoft.com/textbook/glosp.html#Pearson%20Correlation
Sacha
la source
2
Que diriez-vous du coefficient de corrélation de deux dictionnaires?!
user702846
2
@ user702846 La corrélation de Pearson est définie sur une matrice 2xN. Il n'existe aucune méthode généralement applicable qui convertit deux dictionnaires en une matrice 2xN, mais vous pouvez utiliser le tableau de paires de valeurs de dictionnaire correspondant aux clés de l'intersection des clés de vos dictionnaires.
winerd
115

La corrélation de Pearson peut être calculée avec numpy corrcoef.

import numpy
numpy.corrcoef(list1, list2)[0, 1]
winerd
la source
la sortie est déroutante, mais en fait très simple. vérifier cette explication stackoverflow.com/a/3425548/1245622
linqu
56

Une alternative peut être une fonction scipy native de linregress qui calcule:

pente: pente de la droite de régression

interception: interception de la droite de régression

valeur r: coefficient de corrélation

p-value: p-value bilatérale pour un test d'hypothèse dont l'hypothèse nulle est que la pente est nulle

stderr: erreur type de l'estimation

Et voici un exemple:

a = [15, 12, 8, 8, 7, 7, 7, 6, 5, 3]
b = [10, 25, 17, 11, 13, 17, 20, 13, 9, 15]
from scipy.stats import linregress
linregress(a, b)

vous renverra:

LinregressResult(slope=0.20833333333333337, intercept=13.375, rvalue=0.14499815458068521, pvalue=0.68940144811669501, stderr=0.50261704627083648)
Salvador Dali
la source
2
Excellente réponse - de loin la plus informative. Fonctionne également avec un pandas à deux rangées.DataFrame:lineregress(two_row_df)
dmeu
Réponse brillante. Très intuitif aussi, si vous y pensez
Raghuram
37

Si vous n'avez pas envie d'installer scipy, j'ai utilisé ce hack rapide, légèrement modifié de Programming Collective Intelligence :

(Modifié pour l'exactitude.)

from itertools import imap

def pearsonr(x, y):
  # Assume len(x) == len(y)
  n = len(x)
  sum_x = float(sum(x))
  sum_y = float(sum(y))
  sum_x_sq = sum(map(lambda x: pow(x, 2), x))
  sum_y_sq = sum(map(lambda x: pow(x, 2), y))
  psum = sum(imap(lambda x, y: x * y, x, y))
  num = psum - (sum_x * sum_y/n)
  den = pow((sum_x_sq - pow(sum_x, 2) / n) * (sum_y_sq - pow(sum_y, 2) / n), 0.5)
  if den == 0: return 0
  return num / den
Jeff Hammerbacher
la source
2
J'ai été surpris de découvrir que cela n'était pas d'accord avec Excel, NumPy et R. Voir stackoverflow.com/questions/3949226/… .
dfrankow
2
Comme un autre commentateur l'a souligné, cela a un bug float / int. Je pense que sum_y / n est une division entière pour les entiers. Si vous utilisez sum_x = float (sum (x)) et sum_y = float (sum (y)), cela fonctionne.
dfrankow
@dfrankow Je pense que c'est parce qu'imap ne peut pas gérer le float. python donne un TypeError: unsupported operand type(s) for -: 'itertools.imap' and 'float'atnum = psum - (sum_x * sum_y/n)
alvas
4
Comme note de style, Python fronce les sourcils sur cette utilisation inutile de la carte (en faveur de la compréhension des listes)
Maxim Khesin
14
Juste comme un commentaire, considérez que les bibliothèques comme scipy et al sont développées par des gens connaissant beaucoup d'analyse numérique. Cela peut vous éviter de nombreux pièges courants (par exemple, avoir de très grands et très petits nombres en X ou Y peut entraîner une annulation catastrophique)
geekazoid
32

Le code suivant est une interprétation directe de la définition :

import math

def average(x):
    assert len(x) > 0
    return float(sum(x)) / len(x)

def pearson_def(x, y):
    assert len(x) == len(y)
    n = len(x)
    assert n > 0
    avg_x = average(x)
    avg_y = average(y)
    diffprod = 0
    xdiff2 = 0
    ydiff2 = 0
    for idx in range(n):
        xdiff = x[idx] - avg_x
        ydiff = y[idx] - avg_y
        diffprod += xdiff * ydiff
        xdiff2 += xdiff * xdiff
        ydiff2 += ydiff * ydiff

    return diffprod / math.sqrt(xdiff2 * ydiff2)

Tester:

print pearson_def([1,2,3], [1,5,7])

Retour

0.981980506062

Cela est d'accord avec Excel, cette calculatrice , SciPy (également NumPy ), qui renvoient respectivement 0,981980506 et 0,9819805060619657 et 0,98198050606196574.

R :

> cor( c(1,2,3), c(1,5,7))
[1] 0.9819805

ÉDITER : correction d'un bug signalé par un commentateur.

dfrankow
la source
4
Méfiez-vous du type des variables! Vous avez rencontré un problème int / float. En sum(x) / len(x)vous divisez les pouces, pas les flotteurs. Donc sum([1,5,7]) / len([1,5,7]) = 13 / 3 = 4, selon la division entière (alors que vous voulez 13. / 3. = 4.33...). Pour le corriger, réécrivez cette ligne sous la forme float(sum(x)) / float(len(x))(un flottant suffit, car Python la convertit automatiquement).
Piotr Migdal
Votre code ne fonctionnera pas pour les cas tels que: [10,10,10], [0,0,0] ou [10,10], [10,0]. ou même [10,10], [10,10]
madCode
4
Le coefficient de corrélation n'est défini pour aucun de ces cas. Les mettre en R renvoie "NA" pour les trois.
dfrankow
28

Vous pouvez aussi le faire avec pandas.DataFrame.corr:

import pandas as pd
a = [[1, 2, 3],
     [5, 6, 9],
     [5, 6, 11],
     [5, 6, 13],
     [5, 3, 13]]
df = pd.DataFrame(data=a)
df.corr()

Cela donne

          0         1         2
0  1.000000  0.745601  0.916579
1  0.745601  1.000000  0.544248
2  0.916579  0.544248  1.000000
Martin Thoma
la source
5
Ceci est juste une corrélation sans signification
Ivelin
12

Plutôt que de compter sur numpy / scipy, je pense que ma réponse devrait être la plus simple à coder et à comprendre les étapes de calcul du coefficient de corrélation de Pearson (PCC).

import math

# calculates the mean
def mean(x):
    sum = 0.0
    for i in x:
         sum += i
    return sum / len(x) 

# calculates the sample standard deviation
def sampleStandardDeviation(x):
    sumv = 0.0
    for i in x:
         sumv += (i - mean(x))**2
    return math.sqrt(sumv/(len(x)-1))

# calculates the PCC using both the 2 functions above
def pearson(x,y):
    scorex = []
    scorey = []

    for i in x: 
        scorex.append((i - mean(x))/sampleStandardDeviation(x)) 

    for j in y:
        scorey.append((j - mean(y))/sampleStandardDeviation(y))

# multiplies both lists together into 1 list (hence zip) and sums the whole list   
    return (sum([i*j for i,j in zip(scorex,scorey)]))/(len(x)-1)

L' importance du PCC est essentiellement de vous montrer à quel point les deux variables / listes sont fortement corrélées . Il est important de noter que la valeur PCC varie de -1 à 1 . Une valeur comprise entre 0 et 1 indique une corrélation positive. Valeur de 0 = variation la plus élevée (pas de corrélation du tout). Une valeur comprise entre -1 et 0 indique une corrélation négative.

compski
la source
2
Notez que Python a une sumfonction intégrée .
bfontaine
5
Il a une complexité incroyable et des performances lentes sur 2 listes avec plus de 500 valeurs.
Nikolay Fominyh
9

Calcul du coefficient de Pearson à l'aide de pandas en python: je suggère d'essayer cette approche car vos données contiennent des listes. Il sera facile d'interagir avec vos données et de les manipuler depuis la console puisque vous pouvez visualiser votre structure de données et la mettre à jour comme vous le souhaitez. Vous pouvez également exporter l'ensemble de données et l'enregistrer et ajouter de nouvelles données hors de la console python pour une analyse ultérieure. Ce code est plus simple et contient moins de lignes de code. Je suppose que vous avez besoin de quelques lignes de code rapides pour filtrer vos données pour une analyse plus approfondie

Exemple:

data = {'list 1':[2,4,6,8],'list 2':[4,16,36,64]}

import pandas as pd #To Convert your lists to pandas data frames convert your lists into pandas dataframes

df = pd.DataFrame(data, columns = ['list 1','list 2'])

from scipy import stats # For in-built method to get PCC

pearson_coef, p_value = stats.pearsonr(df["list 1"], df["list 2"]) #define the columns to perform calculations on
print("Pearson Correlation Coefficient: ", pearson_coef, "and a P-value of:", p_value) # Results 

Cependant, vous n'avez pas publié vos données pour que je puisse voir la taille de l'ensemble de données ou les transformations qui pourraient être nécessaires avant l'analyse.

Web Ster
la source
Bonjour, bienvenue sur StackOverflow! Essayez d'ajouter une courte description de la raison pour laquelle vous avez choisi ce code et comment il s'applique dans ce cas au début de votre réponse!
Tristo
8

Hmm, beaucoup de ces réponses ont un code long et difficile à lire ...

Je suggère d'utiliser numpy avec ses fonctionnalités astucieuses lorsque vous travaillez avec des tableaux:

import numpy as np
def pcc(X, Y):
   ''' Compute Pearson Correlation Coefficient. '''
   # Normalise X and Y
   X -= X.mean(0)
   Y -= Y.mean(0)
   # Standardise X and Y
   X /= X.std(0)
   Y /= Y.std(0)
   # Compute mean product
   return np.mean(X*Y)

# Using it on a random example
from random import random
X = np.array([random() for x in xrange(100)])
Y = np.array([random() for x in xrange(100)])
pcc(X, Y)
Martin F Thomsen
la source
Bien que j'aime beaucoup cette réponse, je vous conseille de copier / cloner X et Y à l'intérieur de la fonction. Sinon, les deux sont modifiés, ce qui n'est peut-être pas un comportement souhaité.
antonimmo
7

Il s'agit d'une implémentation de la fonction de corrélation de Pearson utilisant numpy:


def corr(data1, data2):
    "data1 & data2 should be numpy arrays."
    mean1 = data1.mean() 
    mean2 = data2.mean()
    std1 = data1.std()
    std2 = data2.std()

#     corr = ((data1-mean1)*(data2-mean2)).mean()/(std1*std2)
    corr = ((data1*data2).mean()-mean1*mean2)/(std1*std2)
    return corr

Mojtaba Khodadadi
la source
7

Voici une variante de la réponse de mkh qui s'exécute beaucoup plus rapidement qu'elle et scipy.stats.pearsonr, en utilisant numba.

import numba

@numba.jit
def corr(data1, data2):
    M = data1.size

    sum1 = 0.
    sum2 = 0.
    for i in range(M):
        sum1 += data1[i]
        sum2 += data2[i]
    mean1 = sum1 / M
    mean2 = sum2 / M

    var_sum1 = 0.
    var_sum2 = 0.
    cross_sum = 0.
    for i in range(M):
        var_sum1 += (data1[i] - mean1) ** 2
        var_sum2 += (data2[i] - mean2) ** 2
        cross_sum += (data1[i] * data2[i])

    std1 = (var_sum1 / M) ** .5
    std2 = (var_sum2 / M) ** .5
    cross_mean = cross_sum / M

    return (cross_mean - mean1 * mean2) / (std1 * std2)
Les tortues sont mignonnes
la source
5

Voici une implémentation pour la corrélation de Pearson basée sur un vecteur clairsemé. Les vecteurs sont ici exprimés sous la forme d'une liste de tuples exprimée en (indice, valeur). Les deux vecteurs clairsemés peuvent être de longueur différente mais sur toute la taille du vecteur devra être la même. Ceci est utile pour les applications d'exploration de texte où la taille du vecteur est extrêmement grande car la plupart des fonctionnalités sont des sacs de mots et donc les calculs sont généralement effectués à l'aide de vecteurs clairsemés.

def get_pearson_corelation(self, first_feature_vector=[], second_feature_vector=[], length_of_featureset=0):
    indexed_feature_dict = {}
    if first_feature_vector == [] or second_feature_vector == [] or length_of_featureset == 0:
        raise ValueError("Empty feature vectors or zero length of featureset in get_pearson_corelation")

    sum_a = sum(value for index, value in first_feature_vector)
    sum_b = sum(value for index, value in second_feature_vector)

    avg_a = float(sum_a) / length_of_featureset
    avg_b = float(sum_b) / length_of_featureset

    mean_sq_error_a = sqrt((sum((value - avg_a) ** 2 for index, value in first_feature_vector)) + ((
        length_of_featureset - len(first_feature_vector)) * ((0 - avg_a) ** 2)))
    mean_sq_error_b = sqrt((sum((value - avg_b) ** 2 for index, value in second_feature_vector)) + ((
        length_of_featureset - len(second_feature_vector)) * ((0 - avg_b) ** 2)))

    covariance_a_b = 0

    #calculate covariance for the sparse vectors
    for tuple in first_feature_vector:
        if len(tuple) != 2:
            raise ValueError("Invalid feature frequency tuple in featureVector: %s") % (tuple,)
        indexed_feature_dict[tuple[0]] = tuple[1]
    count_of_features = 0
    for tuple in second_feature_vector:
        count_of_features += 1
        if len(tuple) != 2:
            raise ValueError("Invalid feature frequency tuple in featureVector: %s") % (tuple,)
        if tuple[0] in indexed_feature_dict:
            covariance_a_b += ((indexed_feature_dict[tuple[0]] - avg_a) * (tuple[1] - avg_b))
            del (indexed_feature_dict[tuple[0]])
        else:
            covariance_a_b += (0 - avg_a) * (tuple[1] - avg_b)

    for index in indexed_feature_dict:
        count_of_features += 1
        covariance_a_b += (indexed_feature_dict[index] - avg_a) * (0 - avg_b)

    #adjust covariance with rest of vector with 0 value
    covariance_a_b += (length_of_featureset - count_of_features) * -avg_a * -avg_b

    if mean_sq_error_a == 0 or mean_sq_error_b == 0:
        return -1
    else:
        return float(covariance_a_b) / (mean_sq_error_a * mean_sq_error_b)

Tests unitaires:

def test_get_get_pearson_corelation(self):
    vector_a = [(1, 1), (2, 2), (3, 3)]
    vector_b = [(1, 1), (2, 5), (3, 7)]
    self.assertAlmostEquals(self.sim_calculator.get_pearson_corelation(vector_a, vector_b, 3), 0.981980506062, 3, None, None)

    vector_a = [(1, 1), (2, 2), (3, 3)]
    vector_b = [(1, 1), (2, 5), (3, 7), (4, 14)]
    self.assertAlmostEquals(self.sim_calculator.get_pearson_corelation(vector_a, vector_b, 5), -0.0137089240555, 3, None, None)
Vipul Sharma
la source
3

J'ai une solution très simple et facile à comprendre pour cela. Pour deux tableaux de longueur égale, le coefficient de Pearson peut être facilement calculé comme suit:

def manual_pearson(a,b):
"""
Accepts two arrays of equal length, and computes correlation coefficient. 
Numerator is the sum of product of (a - a_avg) and (b - b_avg), 
while denominator is the product of a_std and b_std multiplied by 
length of array. 
"""
  a_avg, b_avg = np.average(a), np.average(b)
  a_stdev, b_stdev = np.std(a), np.std(b)
  n = len(a)
  denominator = a_stdev * b_stdev * n
  numerator = np.sum(np.multiply(a-a_avg, b-b_avg))
  p_coef = numerator/denominator
  return p_coef
aumpen
la source
1

Vous vous demandez peut-être comment interpréter votre probabilité dans le contexte de la recherche d'une corrélation dans une direction particulière (corrélation négative ou positive). Voici une fonction que j'ai écrite pour vous aider. Ça pourrait même être vrai!

Il est basé sur les informations que j'ai glanées sur http://www.vassarstats.net/rsig.html et http://en.wikipedia.org/wiki/Student%27s_t_distribution , grâce à d'autres réponses publiées ici.

# Given (possibly random) variables, X and Y, and a correlation direction,
# returns:
#  (r, p),
# where r is the Pearson correlation coefficient, and p is the probability
# that there is no correlation in the given direction.
#
# direction:
#  if positive, p is the probability that there is no positive correlation in
#    the population sampled by X and Y
#  if negative, p is the probability that there is no negative correlation
#  if 0, p is the probability that there is no correlation in either direction
def probabilityNotCorrelated(X, Y, direction=0):
    x = len(X)
    if x != len(Y):
        raise ValueError("variables not same len: " + str(x) + ", and " + \
                         str(len(Y)))
    if x < 6:
        raise ValueError("must have at least 6 samples, but have " + str(x))
    (corr, prb_2_tail) = stats.pearsonr(X, Y)

    if not direction:
        return (corr, prb_2_tail)

    prb_1_tail = prb_2_tail / 2
    if corr * direction > 0:
        return (corr, prb_1_tail)

    return (corr, 1 - prb_1_tail)
Joshua Richardson
la source
1

Vous pouvez consulter cet article. Il s'agit d'un exemple bien documenté pour calculer la corrélation basée sur les données historiques des paires de devises forex à partir de plusieurs fichiers à l'aide de la bibliothèque pandas (pour Python), puis générer un tracé de carte thermique à l'aide de la bibliothèque seaborn.

http://www.tradinggeeks.net/2015/08/calculating-correlation-in-python/

Oleg K
la source
0
def pearson(x,y):
  n=len(x)
  vals=range(n)

  sumx=sum([float(x[i]) for i in vals])
  sumy=sum([float(y[i]) for i in vals])

  sumxSq=sum([x[i]**2.0 for i in vals])
  sumySq=sum([y[i]**2.0 for i in vals])

  pSum=sum([x[i]*y[i] for i in vals])
  # Calculating Pearson correlation
  num=pSum-(sumx*sumy/n)
  den=((sumxSq-pow(sumx,2)/n)*(sumySq-pow(sumy,2)/n))**.5
  if den==0: return 0
  r=num/den
  return r
La source
la source
Les réponses uniquement codées ne sont pas considérées comme une bonne pratique. Veuillez envisager d'ajouter quelques mots pour expliquer comment votre code répond à la question. (lire la page d'aide pour savoir comment répondre à une question sur SO)
Yannis