이전 포스트에 이어서 작성하는 내용입니다.
2022.02.08 - [공부/통계학] - 기초 통계 (분산) python
이번에는 상관계수에 대해 알아보고자 합니다.
피어슨 상관계수 (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 동순위가 많은 데이터에 일반적으로 사용한다고 합니다.
'공부 > 기초통계' 카테고리의 다른 글
표본과 모집단의 이해 (0) | 2022.03.21 |
---|---|
오차, 잔차, 편차의 차이 (기초통계) python (0) | 2022.03.19 |
confusion matrix (혼동행렬) python (0) | 2022.03.17 |
Distribution (분포도) python (0) | 2022.02.10 |
기초 통계 (분산) python (0) | 2022.02.08 |
댓글