본문 바로가기
공부/기초통계

기초통계 (상관계수) python

by signature95 2022. 2. 9.
728x90
반응형

이전 포스트에 이어서 작성하는 내용입니다.

2022.02.08 - [공부/통계학] - 기초 통계 (분산) python

 

기초 통계 (분산) python

이전 포스트에 이어서 작성하는 내용입니다. 2022.02.04 - [공부/통계학] - 기초 통계 (중심도 이해) python 기초 통계 (중심도 이해) python 이번에는 기초 통계에 대해서 알아보도록 하겠습니다. 대표적

signature95.tistory.com

 

이번에는 상관계수에 대해 알아보고자 합니다.

 

피어슨 상관계수 (Pearson's correlation coefficient)

 

공식은 다음과 같습니다.

 

모집단의 피어슨 상관계수

 

표본집단의 피어슨 상관계수 (자유도로 나눠주는 것을 확인할 수 있음)

 

피어슨 상관계수는 공식에서 확인할 수 있듯이 공분산 COV(X,Y)를 각각의 표준편차 곱으로 나눠준 값입니다. 

 

특징

  • -1 ~ 1 사이의 값을 가진다 (±1 값을 가지는 경우에는 완전한 선형관계를 이룬다)
  • 0 의 값을 가지는 경우에는 상관성이 없다. 
  • 이상치, 극단치를 가지는 데이터의 경우, 결과가 왜곡될 가능성이 높다.

가정

  • 두 변수는 연속형 변수이다.
  • 두 변수는 정규분포를 따른다. (정규성 검정이 필요하다.)
  • 두 변수는 선형 관계를 가진다.

그렇다면 코드로 실습을 해보겠습니다.

 

데이터는 보스턴 집 값 데이터로 구성되어 있습니다.

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import scipy as sp

data = pd.read_csv("https://raw.githubusercontent.com/signature95/tistory/main/dataset/boston.csv")
print(data)

>>>

        CRIM    ZN  INDUS  CHAS    NOX     RM   AGE     DIS  RAD  TAX  \
0    0.00632  18.0   2.31     0  0.538  6.575  65.2  4.0900    1  296   
1    0.02731   0.0   7.07     0  0.469  6.421  78.9  4.9671    2  242   
2    0.02729   0.0   7.07     0  0.469  7.185  61.1  4.9671    2  242   
3    0.03237   0.0   2.18     0  0.458  6.998  45.8  6.0622    3  222   
4    0.06905   0.0   2.18     0  0.458  7.147  54.2  6.0622    3  222   
..       ...   ...    ...   ...    ...    ...   ...     ...  ...  ...   
501  0.06263   0.0  11.93     0  0.573  6.593  69.1  2.4786    1  273   
502  0.04527   0.0  11.93     0  0.573  6.120  76.7  2.2875    1  273   
503  0.06076   0.0  11.93     0  0.573  6.976  91.0  2.1675    1  273   
504  0.10959   0.0  11.93     0  0.573  6.794  89.3  2.3889    1  273   
505  0.04741   0.0  11.93     0  0.573  6.030  80.8  2.5050    1  273   

     PTRATIO       B  LSTAT  MEDV  CAT. MEDV  
0       15.3  396.90   4.98  24.0          0  
1       17.8  396.90   9.14  21.6          0  
2       17.8  392.83   4.03  34.7          1  
3       18.7  394.63   2.94  33.4          1  
4       18.7  396.90   5.33  36.2          1  
..       ...     ...    ...   ...        ...  
501     21.0  391.99   9.67  22.4          0  
502     21.0  396.90   9.08  20.6          0  
503     21.0  396.90   5.64  23.9          0  
504     21.0  393.45   6.48  22.0          0  
505     21.0  396.90   7.88  11.9          0  

[506 rows x 15 columns]

 

먼저 상관분석을 본격적으로 하기 앞서, 시각화를 통해 육안으로 확인하는 작업이 필요합니다.

이는 seaborn의 pairplot을 활용하였습니다.

 

참고로 MEDV는 집값의 중위값을 의미하고, NOX는 10ppm 당 농축 이산화질소를 의미합니다.

 

# 상관분석에 앞서 먼저 시각화를 이용하여 상관성을 육안으로 확인해본다.
sub = data[{'MEDV', 'NOX'}]

# 각 데이터의 히스토그램과 산점도를 도출 (산점도에는 회귀선까지 구현 kind='reg'를 사용)
sns.pairplot(sub, kind='reg')
plt.show()

 

회귀선을 도출해본 결과, 음의 상관성이 일부 나타나는 것을 확인할 수 있습니다.

 

def cov_pearson(data1, data2, population = True):
    # 라이브러리 호출
    import numpy as np
    from scipy.stats import pearsonr

    # 변수를 설정하는 부분입니다.
    mu_1 = np.sum(data1)/len(data1)
    mu_2 = np.sum(data2)/len(data2)
    var_1 = np.sum([pow(i - mu_1, 2) for i in data1]) / (len(data1))
    var_2 = np.sum([pow(i - mu_2, 2) for i in data2]) / (len(data2))
    std_1 = var_1 ** 0.5
    std_2 = var_2 ** 0.5
    
    # 데이터가 모수인지 표본인지에 따라 자유도를 산입할지 여부를 결정합니다.
    if population == True:
        covariance = sum( (data1 - mu_1) * (data2 - mu_2) ) / (len(data1))
        correlation = covariance / (std_1 * std_2)
        print(f'모집단 data 각각의 분산 값은 COV(X,X) = {round(var_1,3)}, COV(Y,Y) = {round(var_2,3)}(으)로 계산됩니다.')
        print(f'모집단 data에 대한 공분산 값 : {round(covariance,3)}')
        print(f'모집단 data에 대한 피어슨 상관계수 값 : {round(correlation,3)}')
    else:
        var_1 = np.sum([pow(i - mu_1, 2) for i in data1]) / (len(data1) - 1)
        var_2 = np.sum([pow(i - mu_2, 2) for i in data2]) / (len(data2) - 1)
        std_1 = var_1 ** 0.5
        std_2 = var_2 ** 0.5
        covariance_ddof = sum( (data1 - mu_1) * (data2 - mu_2) ) / (len(data1) -1)
        correlation_ddof = covariance_ddof / (std_1 * std_2)
        print(f'표본 data 각각의 추정된 분산 값은 COV(x,x) = {round(var_1,3)}, COV(y,y) = {round(var_2,3)}(으)로 계산됩니다.')
        print(f'표본 data로 추정한 모집단의 공분산 값 : {round(covariance_ddof,3)}')
        print(f'표본 data로 추정한 모집단의 피어슨 상관계수 값 : {round(correlation_ddof,3)}')

    # Scipy 라이브러리에서 구한 피어슨 계수를 통해 위에서 확인한 상관계수 값을 검증해봅니다.
    print(f'Scipy로 구한 피어슨 상관계수는 {round(pearsonr(data1, data2)[0], 3)}입니다.')
    
    # Scipy에서 제공하는 피어슨 검정의 P값을 이용하여 데이터 간의 독립성에 대한 검정 결과 해석을 출력합니다.
    if pearsonr(data1, data2)[1] < 0.05:
        print(f'Scipy는 각 데이터의 상관계수가 유의한지 판단합니다. 위 검정의 p-value는 {round(pearsonr(data1, data2)[1],5) * 100}%로 두 데이터의 연관성이 없다는 귀무가설을 5%의 유의수준으로 기각할 수 있습니다.')
    else:
        print(f'Scipy는 각 데이터의 상관계수가 유의한지 판단합니다. 위 검정의 p-value는 {round(pearsonr(data1, data2)[1],5) * 100}%로 두 데이터의 연관성이 없다는 귀무가설을 5%의 유의수준으로 기각할 수 없습니다.')

cov_pearson(data['MEDV'], data['NOX'], False)

>>>

표본 data 각각의 추정된 분산 값은 COV(x,x) = 84.587, COV(y,y) = 0.013(으)로 계산됩니다.
표본 data로 추정한 모집단의 공분산 값 : -0.455
표본 data로 추정한 모집단의 피어슨 상관계수 값 : -0.427
Scipy로 구한 피어슨 상관계수는 -0.427입니다.
Scipy는 각 데이터의 상관계수가 유의한지 판단합니다. 위 검정의 p-value는 0.0%로 두 데이터의 연관성이 없다는 귀무가설을 5%의 유의수준으로 기각할 수 있습니다.

물론 피어슨 상관계수를 Scipy 라이브러리를 활용하여 도출할 수 있으나, 공식을 활용하여 직접 구해보는 작업도 진행해 보았습니다.

 

 

하지만, 사실 피어슨 상관계수는 정규성이 담보되어야 합니다. 

따라서 정규성 검정 중 하나인 shaprio-test를 진행해봅니다. (물론 실제로는 정규성 검정을 먼저 시행해야 하지만 포스트의 특성상 피어슨 상관계수를 먼저 도출해보기 위해 순서를 바꾸었습니다.)

 

# 라이브러리 호출 
from scipy.stats import shapiro # 정규분포를 따르도록 랜덤하게 값을 부여 

for i in ['MEDV', 'NOX']:
    print(shapiro(data[i]))
    
>>>

ShapiroResult(statistic=0.9171748757362366, pvalue=4.940195786292171e-16)
ShapiroResult(statistic=0.9356356263160706, pvalue=5.775580887027246e-14)

 

정규성을 검정해본 결과, p값이 매우 낮게 나왔습니다. 이는 두 데이터가 정규성을 충족한다는 귀무가설을 매우 강하게 기각할 수 있습니다.

따라서 이번에는 두 데이터의 스피어만 상관계수를 구해보도록 하겠습니다.

 

 

스피어만 상관계수 (Spearman's correlation coefficient)

스피어만 상관계수 공식

D는 순위의 차이를 의미합니다. 

 

밑 표 참고)

변수 X 변수 Y 변수 X의 순위 변수 Y의 순위 순위 차이 (D)
10 40 2 3 -1
20 87 3 4 -1
1 15 1 2 -1
90 1 4 1 3

 

특징은 앞서 소개한 피어슨 상관계수와 같습니다. 하지만, 가정이 조금 다릅니다.

 

가정

  • 두 변수는 적어도 순서형 변수이다.
  • 두 변수는 단조 관계를 가진다. (증감에 대한 방향성을 유지하는 형태로 일대일 대응함수를 생각하면 된다)

이처럼 가정에서 차이를 보이기 때문에, 스피어만 계수는 피어슨보다 좀 더 완화된 조건이 적용됩니다.

먼저 정규성 검정을 진행할 필요가 없습니다.

또한, 이상치와 극단치 값에도 순서 척도를 적용하기에 민감하지 않습니다.

 

그렇다면 위 표의 데이터를 잠깐 활용하여 스피어만 상관계수 공식을 적용해보고 scipy에서 제공하는 스피어만 방법으로 도출하여 비교해보겠습니다.

# 위의 표를 참고 (diff는 변수 X의 순위 - 변수 Y의 순위를 의미함)
X = [10,20,1,90]
Y = [40,87,15,1]
diff = [-1, -1, -1, 3]
n = 4

sum_diff = 6 * sum([pow(i, 2) for i in diff])
spearman = 1 - sum_diff/(n * (n ** 2 -1))
print(spearman)

# scipy를 활용한 스피어만 상관계수 출력
from scipy.stats import spearmanr
print(spearmanr(X,Y)[0])

>>>

-0.19999999999999996
-0.19999999999999998

이처럼 스피어만 상관계수를 도출할 수 있습니다. 

 

한번 더 나아가, X,Y 데이터의 피어슨 상관계수를 구하면, -0.452가 도출됩니다. (극단치 값의 영향을 받았기 때문)

X = [10,20,1,90]
Y = [40,87,15,1]

# 극단치가 있는 X,Y에 대해 피어슨 상관계수를 도출해봄 (위의 함수를 사용)
cov_pearson(X,Y,False)

>>>

표본 data 각각의 추정된 분산 값은 COV(x,x) = 1646.917, COV(y,y) = 1427.583(으)로 계산됩니다.
표본 data로 추정한 모집단의 공분산 값 : -693.583
표본 data로 추정한 모집단의 피어슨 상관계수 값 : -0.452
Scipy로 구한 피어슨 상관계수는 -0.452입니다.
Scipy는 각 데이터의 상관계수가 유의한지 판단합니다. 위 검정의 p-value는 54.766000000000005%로 두 데이터의 연관성이 없다는 귀무가설을 5%의 유의수준으로 기각할 수 없습니다.

 

이제 공식은 적용해보았으니, 다시 돌아와서 보스턴 집 값 데이터에 대해 스피어만 상관계수를 구해보겠습니다.

 

여기서는 데이터의 양이 많아서 공식을 적용하진 않고 scipy 라이브러리의 매서드를 활용하였습니다.

 

from scipy.stats import spearmanr

spearmanr(data['MEDV'], data['NOX'])

>>>

SpearmanrResult(correlation=-0.5626088297953195, pvalue=1.4059853091979118e-43)

 

위 결과는 아까 피어슨 상관계수였던 -0.427 보다 더 높은 값의 상관성이 도출되었습니다.

또한, 스피어만 상관계수의 귀무가설은 두 변수가 관련성이 없다 (상관계수 = 0)로 위 결과는 귀무가설을 강하게 기각합니다.

 

 

마지막으로는 켄달의 상관계수입니다.

 

켄달 상관계수 (Kendall's correlation coefficient)

 

켄달 상관계수의 공식은 다음과 같습니다.

 

Kendall’s Tau = (C – D / C + D)

C = 부합 데이터, D = 비부합 데이터

 

부합 데이터는 concordant pair, 비부합 데이터는 disconcordant pair를 의미합니다.

 

이를 좀 더 자세하게 설명하면 다음과 같습니다.

 

변수 X 변수 Y 변수 X의 순위 변수 Y의 순위
12 1 5 2
2 4 2 4
1 7 1 5
11 2 4 3
3 0 3 1

 

먼저 위의 표를 X변수를 기준으로 오름차순 정렬합니다. 

변수 X (순위) : 오름차순 기준 변수 Y (순위) concordant pair 수 disconcordant pair 수
1 (1) 7 (5) 0 2
2 (2) 4 (4) 0 1
3 (3) 0 (1) 1 0
11 (4) 2 (3) 0 1
12 (5) 1 (2)    

X 순위가 올라갈수록 (1 -> 2 -> 3 ...) Y순위도 올라가는 것이 현재 row기준으로 몇개나 밑에 있는지를 보고 그 개수가 곧 concordant pair 개수로 생각하시면 됩니다.

 

disconcordant는 X 순위는 올라가는데, 반대로 Y는 내려가는 경우로 방향성이 역일 때입니다.

 

따라서 위 데이터의 켄달 상관계수는 (1-4)/(1+4) = -0.6이 됩니다.

 

이를 코드로 보면 다음과 같습니다.

 

X = [12, 2, 1, 11, 3]
Y = [1, 4, 7, 2, 0]

from scipy.stats import kendalltau

kendalltau(X,Y)

>>>

KendalltauResult(correlation=-0.6, pvalue=0.23333333333333334)

 

보통 켄달 상관계수는 소표본 or 동순위가 많은 데이터에 일반적으로 사용한다고 합니다.

 

728x90

댓글