Calcolo della correlazione e del significato di Pearson in Python


Risposte:


202

Puoi dare un'occhiata a 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

2
Che ne dici di coefficiente di correlazione di due dizionari ?!
user702846

2
@ user702846 La correlazione di Pearson è definita su una matrice 2xN. Non esiste un metodo generalmente applicabile che converte due dizionari in una matrice 2xN, ma è possibile utilizzare la matrice di coppie di valori di dizionario corrispondenti alle chiavi dell'intersezione delle chiavi dei dizionari.
Winerd


56

Un'alternativa può essere una funzione scipy nativa da Linregress che calcola:

pendenza: pendenza della linea di regressione

intercetta: intercetta la linea di regressione

valore r: coefficiente di correlazione

valore p: valore p bilaterale per un test di ipotesi la cui ipotesi nulla è che la pendenza sia zero

stderr: errore standard del preventivo

Ed ecco un esempio:

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)

ti restituirà:

LinregressResult(slope=0.20833333333333337, intercept=13.375, rvalue=0.14499815458068521, pvalue=0.68940144811669501, stderr=0.50261704627083648)

2
Ottima risposta - di gran lunga la più istruttiva. Funziona anche con un panda a due file.DataFrame:lineregress(two_row_df)
dmeu

Risposta brillante. Anche molto intuitivo, se ci pensate
Raghuram

37

Se non hai voglia di installare scipy, ho usato questo trucco rapido, leggermente modificato da Programming Collective Intelligence :

(Modificato per correttezza.)

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

2
Sono stato sorpreso di scoprire questo in disaccordo con Excel, NumPy e R. Vedi stackoverflow.com/questions/3949226/… .
domenica

2
Come ha sottolineato un altro commentatore, questo ha un bug float / int. Penso che sum_y / n sia una divisione intera per ints. Se usi sum_x = float (sum (x)) e sum_y = float (sum (y)), funziona.
domenica

@dfrankow Penso che sia perché imap non può gestire il float. python dà un TypeError: unsupported operand type(s) for -: 'itertools.imap' and 'float'atnum = psum - (sum_x * sum_y/n)
alvas il

4
Come nota di stile, Python aggrotta le sopracciglia su questo uso non necessario della mappa (a favore della comprensione dell'elenco)
Maxim Khesin,

14
Proprio come un commento, considera che le biblioteche come scipy et al sono sviluppate da persone che conoscono molta analisi numerica. Questo può evitare molte insidie ​​comuni (ad esempio, avere numeri molto grandi e molto piccoli in X o Y può comportare la cancellazione catastrofica)
geekazoid

32

Il seguente codice è una chiara interpretazione della definizione :

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)

Test:

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

ritorna

0.981980506062

Questo è d'accordo con Excel, questo calcolatore , SciPy (anche NumPy ), che restituisce rispettivamente 0,981980506 e 0,9819805060619657 e 0,98198050606196574.

R :

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

MODIFICARE : corretto un bug segnalato da un commentatore.


4
Fai attenzione al tipo di variabili! Hai riscontrato un problema int / float. In sum(x) / len(x)te dividi ints, non float. Quindi sum([1,5,7]) / len([1,5,7]) = 13 / 3 = 4, secondo la divisione intera (mentre tu vuoi 13. / 3. = 4.33...). Per risolverlo riscrivi questa riga come float(sum(x)) / float(len(x))(basta un float, poiché Python lo converte automaticamente).
Piotr Migdal,

Il tuo codice non funzionerà per casi come: [10,10,10], [0,0,0] o [10,10], [10,0]. o anche [10,10], [10,10]
madCode

4
Il coefficiente di correlazione non è definito per nessuno di questi casi. Metterli in R restituisce "NA" per tutti e tre.
dankankow,

28

Puoi farlo anche con 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()

Questo da

          0         1         2
0  1.000000  0.745601  0.916579
1  0.745601  1.000000  0.544248
2  0.916579  0.544248  1.000000

5
Questa è solo una correlazione senza significato
Ivelin,

12

Invece di fare affidamento su numpy / scipy, penso che la mia risposta dovrebbe essere la più semplice da codificare e comprendere i passaggi nel calcolo del coefficiente di correlazione di 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)

Il significato di PCC è sostanzialmente quello di mostrarti quanto siano strettamente correlate le due variabili / liste. È importante notare che il valore PCC varia da -1 a 1 . Un valore compreso tra 0 e 1 indica una correlazione positiva. Valore di 0 = variazione massima (nessuna correlazione di sorta). Un valore compreso tra -1 e 0 indica una correlazione negativa.


2
Nota che Python ha una sumfunzione integrata.
bfontaine,

5
Ha una straordinaria complessità e prestazioni lente su 2 elenchi con oltre 500 valori.
Nikolay Fominyh,

9

Calcolo del coefficiente di Pearson usando i panda in Python: suggerirei di provare questo approccio poiché i tuoi dati contengono elenchi. Sarà facile interagire con i tuoi dati e manipolarli dalla console poiché puoi visualizzare la struttura dei dati e aggiornarli come desideri. Puoi anche esportare il set di dati e salvarlo e aggiungere nuovi dati dalla console di Python per un'analisi successiva. Questo codice è più semplice e contiene meno righe di codice. Presumo che occorrano alcune brevi righe di codice per schermare i dati per ulteriori analisi

Esempio:

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 

Tuttavia, non hai pubblicato i tuoi dati per me per vedere le dimensioni del set di dati o le trasformazioni che potrebbero essere necessarie prima dell'analisi.


Ciao, benvenuto su StackOverflow! Prova ad aggiungere una breve descrizione del perché hai scelto questo codice e come si applica in questo caso all'inizio della risposta!
Tristo,

8

Mmm, molte di queste risposte hanno un codice lungo e difficile da leggere ...

Suggerirei di usare numpy con le sue eleganti funzionalità quando si lavora con array:

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)

Anche se mi piace molto questa risposta, consiglierei di copiare / clonare X e Y all'interno della funzione. Altrimenti vengono modificati entrambi, il che potrebbe non essere un comportamento desiderato.
antonimmo,

7

Questa è un'implementazione della funzione di correlazione di Pearson usando 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


7

Ecco una variante della risposta di mkh che corre molto più veloce di essa, e scipy.stats.pearsonr, usando 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)

5

Ecco un'implementazione per la correlazione di Pearson basata sul vettore sparse. I vettori qui sono espressi come un elenco di tuple espresse come (indice, valore). I due vettori sparsi possono avere una lunghezza diversa, ma nel complesso le dimensioni del vettore dovranno essere uguali. Ciò è utile per le applicazioni di mining di testo in cui la dimensione del vettore è estremamente grande a causa della maggior parte delle funzioni essendo un sacco di parole e quindi i calcoli vengono solitamente eseguiti utilizzando vettori sparsi.

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)

Test unitari:

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)

3

Ho una soluzione molto semplice e facile da capire per questo. Per due matrici di uguale lunghezza, il coefficiente di Pearson può essere facilmente calcolato come segue:

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

1

Potresti chiederti come interpretare la tua probabilità nel contesto della ricerca di una correlazione in una determinata direzione (correlazione negativa o positiva). Ecco una funzione che ho scritto per aiutarla. Potrebbe anche essere giusto!

Si basa sulle informazioni che ho raccolto da http://www.vassarstats.net/rsig.html e http://en.wikipedia.org/wiki/Student%27s_t_distribution , grazie ad altre risposte pubblicate qui.

# 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)


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

Le risposte di solo codice non sono considerate buone pratiche. Ti consigliamo di aggiungere alcune parole per spiegare come il tuo codice risponde alla domanda. (leggi la pagina di aiuto su come rispondere a una domanda su SO)
Yannis,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.