Расчет корреляции Пирсона и значимости в Python


Ответы:


202

Вы можете взглянуть на 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
Как насчет коэффициента корреляции двух словарей ?!
user702846

2
@ user702846 Корреляция Пирсона определяется на матрице 2xN. Не существует общепринятого метода, который преобразует два словаря в матрицу 2xN, но вы можете использовать массив пар значений словаря, соответствующих ключам пересечения ключей ваших словарей.
Winerd

115

Корреляция Пирсона может быть рассчитана с помощью числа Нумпи corrcoef.

import numpy
numpy.corrcoef(list1, list2)[0, 1]

вывод сбивает с толку, но на самом деле очень просто. проверить это объяснение stackoverflow.com/a/3425548/1245622
linqu

56

Альтернативой может быть встроенная функция scipy из linregress, которая вычисляет:

наклон: наклон линии регрессии

Перехват: перехват линии регрессии

r-значение: коэффициент корреляции

p-значение: двустороннее p-значение для проверки гипотезы, нулевая гипотеза которой состоит в том, что наклон равен нулю

stderr: стандартная ошибка оценки

И вот пример:

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)

вернет вам:

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

2
Отличный ответ - безусловно, самый информативный. Также работает с двухрядными пандами. DataFrame:lineregress(two_row_df)
dmeu

Блестящий ответ. Очень интуитивный тоже, если вы об этом думаете
Raghuram

37

Если вы не хотите устанавливать scipy, я воспользовался этим быстрым взломом, немного измененным из « Программирование Коллективного Разума» :

(Отредактировано для корректности.)

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
Я был удивлен, обнаружив, что это не согласуется с Excel, NumPy и R. См. Stackoverflow.com/questions/3949226/… .
dfrankow

2
Как отметил другой комментатор, в этом есть ошибка типа float / int. Я думаю, что sum_y / n является целочисленным делением для целых. Если вы используете sum_x = float (sum (x)) и sum_y = float (sum (y)), это работает.
dfrankow

@dfrankow Я думаю, это потому, что imap не может справиться с плавающей точкой. Питон дает TypeError: unsupported operand type(s) for -: 'itertools.imap' and 'float'atnum = psum - (sum_x * sum_y/n)
alvas

4
В качестве заметки о стиле Python хмурится из-за этого ненужного использования карты (в пользу понимания списка)
Максим Хесин

14
В качестве комментария рассмотрим, что библиотеки, как scipy et al., Разработаны людьми, знающими много численного анализа. Это может избежать многих распространенных ошибок (например, очень большие и очень маленькие числа в X или Y могут привести к катастрофической отмене)
geekazoid

32

Следующий код является простой интерпретацией определения :

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)

Тест:

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

возвращается

0.981980506062

Это согласуется с Excel, этот калькулятор , SciPy (также NumPy ), который возвращает 0,981980506 и 0,9819805060619657 и 0,98198050606196574 соответственно.

R :

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

РЕДАКТИРОВАТЬ : Исправлена ​​ошибка, указанная комментатором.


4
Остерегайтесь типов переменных! Вы столкнулись с проблемой int / float. В sum(x) / len(x)вас делят целые, а не плавает. Итак sum([1,5,7]) / len([1,5,7]) = 13 / 3 = 4, согласно целочисленному делению (тогда как вы хотите 13. / 3. = 4.33...). Чтобы исправить это, перепишите эту строку как float(sum(x)) / float(len(x))(достаточно одного числа с плавающей запятой, поскольку Python конвертирует его автоматически).
Петр Мигдаль

Ваш код не будет работать в таких случаях, как: [10,10,10], [0,0,0] или [10,10], [10,0]. или даже [10,10], [10,10]
madCode

4
Коэффициент корреляции не определен ни для одного из этих случаев. Помещение их в R возвращает «NA» для всех трех.
dfrankow

28

Вы можете сделать это 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()

Это дает

          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
Это просто корреляция без значения
Ивелин

12

Вместо того, чтобы полагаться на numpy / scipy, я думаю, что мой ответ должен быть самым простым для кодирования и понимания шагов по вычислению коэффициента корреляции Пирсона (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)

Значение ОКК в основном , чтобы показать вам , как сильно коррелируют две переменные / списки. Важно отметить, что значение PCC составляет от -1 до 1 . Значение от 0 до 1 обозначает положительную корреляцию. Значение 0 = наибольшее отклонение (без какой-либо корреляции). Значение от -1 до 0 обозначает отрицательную корреляцию.


2
Обратите внимание, что в Python есть встроенная sumфункция.
bfontaine

5
Он имеет удивительную сложность и медленную производительность в 2 списках с 500+ значениями.
Николай Фоминых

9

Расчет коэффициента Пирсона с использованием панд в python: я бы предложил попробовать этот подход, поскольку ваши данные содержат списки. Будет легко взаимодействовать с вашими данными и манипулировать ими из консоли, поскольку вы сможете визуализировать свою структуру данных и обновлять ее по своему желанию. Вы также можете экспортировать набор данных, сохранить его и добавить новые данные из консоли Python для последующего анализа. Этот код проще и содержит меньше строк кода. Я предполагаю, что вам нужно несколько быстрых строк кода для проверки ваших данных для дальнейшего анализа

Пример:

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 

Тем не менее, вы не опубликовали свои данные для меня, чтобы увидеть размер набора данных или преобразования, которые могут потребоваться перед анализом.


Здравствуйте, добро пожаловать в StackOverflow! Попробуйте добавить краткое описание того, почему вы выбрали этот код и как он применяется в этом случае в начале вашего ответа!
Tristo

8

Хм, многие из этих ответов имеют длинный и трудный для чтения код ...

Я бы предложил использовать Numpy с его отличными функциями при работе с массивами:

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)

Хотя мне очень нравится этот ответ, я бы посоветовал скопировать / клонировать X и Y внутри функции. В противном случае оба они изменяются, что может быть нежелательным поведением.
Антониммо

7

Это реализация корреляционной функции Пирсона с использованием 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

Вот вариант ответа mkh, который работает намного быстрее, и scipy.stats.pearsonr, используя 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

Вот реализация корреляции Пирсона на основе разреженного вектора. Векторы здесь выражены в виде списка кортежей, выраженного как (индекс, значение). Два разреженных вектора могут иметь разную длину, но размер вектора должен быть одинаковым. Это полезно для приложений интеллектуального анализа текста, где размер вектора чрезвычайно велик из-за того, что большинство функций является мешком слов, и, следовательно, вычисления обычно выполняются с использованием разреженных векторов.

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)

Модульные тесты:

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

У меня есть очень простое и понятное решение для этого. Для двух массивов одинаковой длины коэффициент Пирсона можно легко вычислить следующим образом:

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

Вы можете задаться вопросом, как интерпретировать свою вероятность в контексте поиска корреляции в определенном направлении (отрицательная или положительная корреляция). Вот функция, которую я написал, чтобы помочь с этим. Это может быть даже правильно!

Он основан на информации, которую я почерпнул из http://www.vassarstats.net/rsig.html и http://en.wikipedia.org/wiki/Student%27s_t_distribution , благодаря другим ответам, размещенным здесь.

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

1

Вы можете взглянуть на эту статью. Это хорошо документированный пример расчета корреляции на основе исторических данных валютных пар форекс из нескольких файлов с использованием библиотеки панд (для Python), а затем создания графика тепловой карты с использованием библиотеки Seaborn.

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


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

Ответы только на код не считаются хорошей практикой. Пожалуйста, добавьте несколько слов, чтобы объяснить, как ваш код решает этот вопрос. (прочитайте страницу справки о том, как ответить на вопрос о SO)
Яннис
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.