본문 바로가기

Mathematics

상관계수

 

 

지난 글에서 공분산이 0임을 비상관으로 정의했다. ‘비상관’은 문자 그대로 서로 관련이 없음을 의미한다. 그렇다면 공분산이 0이 아닌 다른 값이면 두 확률변수는 서로 관련이 있다는 것이다. 그리고 기왕이면 공분산의 절대값이 커질 수록 두 변수 사이의 관련성이 커지면 좋겠다.

이는 공분산으로 두 변수 간의 상관성을 측정하려는 시도라고 볼 수 있다. 그런데 이렇게 하면 문제가 하나 있는데, 변수의 단위가 달라진다는 데 있다. 공분산은 분산의 일반화이므로 분산과 같은 문제점을 안고 있는 것이다.

분산의 경우, 루트를 씌워 표준편차를 정의하여 해결했다. 하지만 영국의 수학자 칼 피어슨(Karl Pearson)은 다른 방식으로 이 문제를 해결했다. 바로 다음과 같이 $X$와 $Y$의 상관계수(Correlation Coefficient; 相关系数) $R(X,Y)$를 정의하는 것이다.

$$ R(X,Y):=\frac{\mathrm{Cov}(X,Y)}{\sqrt{\mathrm{Var}(X)}\sqrt{\mathrm{Var}(Y)}}=\frac{\mathrm{Cov}(X,Y)}{\mathrm{SD}(X)\mathrm{SD}(Y)} $$

이렇게 정의한 상관계수를 정의자의 이름을 따서 피어슨 상관계수(Pearson Correlation Coefficient, PCC; 皮尔逊相关系数)이라고 하며, 흔히 상관계수라 함은 바로 이 피어슨 상관계수를 의미한다.

피어슨 상관계수를 $R(X^{\ast},Y^{\ast})$와 같이 표준화된 변수를 통해 계산하는 것으로 정의하기도 하는데, 아래와 같이 $R(X^{\ast},Y^{\ast})=R(X,Y)$임을 보일 수 있기 때문에 문제되지 않는다. (표준화된 변수의 분산이 1임을 이용한다.)

$$ \begin{split} R(X^{\ast},Y^{\ast})&=\frac{\mathrm{Cov}\left(\frac{X-\mathbb{E}(X)}{\sqrt{\mathrm{Var}(X)}},\frac{Y-\mathbb{E}(Y)}{\sqrt{\mathrm{Var}(Y)}}\right)}{1\cdot1}=\frac{\mathrm{Cov}(X-\mathbb{E}(X),Y-\mathbb{E}(Y))}{\sqrt{\mathrm{Var}(X)}\sqrt{\mathrm{Var}(Y)}} \\ &=\frac{\mathrm{Cov}(X,Y)}{\sqrt{\mathrm{Var}(X)}\sqrt{\mathrm{Var}(Y)}}=R(X,Y) \end{split} $$

note1: 실제 통계분석에서는 여러가지 이유로(예를 들어 분류분석에서 마할라노비스 거리를 이용하기 위해) 먼저 변수를 표준화하는 과정을 거친 뒤 분석에 착수하는 경우가 많은데, 상관계수가 표준화 전후와 관계 없이 같은 값이라는 사실은 행운이다.

note2: 이 결과를 일반화하면 $\vert R(aX+b,cY+d)\vert=\vert R(X,Y)\vert$, 즉 데이터에 선형변환을 하더라도 상관계수의 부호는 바뀔지 몰라도 크기는 불변이다.

피어슨 상관계수는 공분산이 0일 때 0이다. 그리고 변수의 단위가 약분되어 사라지므로 골치 아픈 문제가 하나 해결된다. 이 외에도 하나 더 좋은 점이 있는데, 바로 상관계수의 범위가 -1부터 +1까지라는 것이다.

$$ \begin{split} &\mathrm{Var}\left(\frac{X}{\mathrm{SD}(X)}\pm\frac{Y}{\mathrm{SD}(Y)}\right)=\frac{\mathrm{Var}(X)}{[\mathrm{SD}(X)]^2}+\frac{\mathrm{Var}(Y)}{[\mathrm{SD}(Y)]^2}\pm2\frac{\mathrm{Cov}(X,Y)}{\mathrm{SD}(X)\mathrm{SD}(Y)} \\ &=2\pm2R(X,Y))\ge0 \\ &\therefore -1\le R(X,Y)\le1 \end{split} $$

상관계수가 0의 값을 가진다는 것은 곧 비상관을 의미한다. 그렇다면 양극단의 값인 -1이나 +1일 때는 무슨 의미일까? 우선 다음이 성립한다.

$$ R(X,Y)=+1\Rightarrow\mathrm{Var}\left(\frac{X}{\mathrm{SD}(X)}-\frac{Y}{\mathrm{SD}(Y)}\right)=2-(+2)=0 \\ R(X,Y)=-1\Rightarrow\mathrm{Var}\left(\frac{X}{\mathrm{SD}(X)}+\frac{Y}{\mathrm{SD}(Y)}\right)=2+(-2)=0 $$

그리고 어떤 확률변수의 분산이 0인 것은 그 확률변수가 사실은 상수임을 의미한다. 따라서

$$ R(X,Y)=+1\Rightarrow\frac{X}{\mathrm{SD}(X)}-\frac{Y}{\mathrm{SD}(Y)}=\mathrm{constant}\Rightarrow Y=aX+b(a>0) \\ R(X,Y)=-1\Rightarrow\frac{X}{\mathrm{SD}(X)}+\frac{Y}{\mathrm{SD}(Y)}=\mathrm{constant}\Rightarrow Y=aX+b(a<0) $$

즉, $\mathbb{R}^2$에서 상관계수가 +1이면 $(X,Y)$는 기울기가 양인 직선을 나타내고, 이것을 완벽한(Perfect; 完全) 양의(Positive; 正) 상관관계(Correlation; 相关)라고 한다. 상관계수가 -1이면 $(X,Y)$는 기울기가 음인 직선을 나타내고, 이것을 완벽한 음의(Negative; 负) 상관관계라고 한다. 아무튼 직선을 나타내므로, 완벽한 상관관계를 두고 선형관계(Linear Relation; 线性关系)라고도 한다.

한편, $Y=aX+b(a\not=0)$와 같이 먼저 두 확률변수 간의 선형관계를 가정했을 때 다음과 같은 결론을 얻는다. ($X$와 상수 $b$가 독립임을 기억하자.)

$$ \begin{split} &\mathrm{Cov}(X,Y)=\mathrm{Cov}(X,aX+b)=a\mathrm{Cov}(X,X)+\mathrm{Cov}(X,b)=a\mathrm{Var}(X) \\ &\mathrm{Var}(Y)=\mathrm{Var}(aX+b)=a^2\mathrm{Var}(X) \\ &\therefore R(X,Y)=\frac{a\mathrm{Var}(X)}{\sqrt{\mathrm{Var}(X)}\sqrt{a^2\mathrm{Var}(X)}}=\frac{a}{\vert a\vert}=\pm1 \end{split} $$

따라서 두 변수 사이에 선형관계가 존재한다는 것은 상관계수의 절댓값이 1인 것과 동치이다.

$$ Y=aX+b(a\not=0)\Leftrightarrow\vert R(X,Y)\vert=1 $$

#2. Python으로 상관계수 구하기

두 확률변수의 관측값을 알고 있다면 Python의 pandas 모듈로 상관계수를 쉽게 구할 수 있다. 어떤 사람의 한 달 소득 $Y$와 소비 $C$의 관련성을 알아보기 위해 상관계수 $R(Y,C)$를 구해보자. 우선 다음과 같은 코드를 실행시킨다.

import pandas as pd

y = [100, 300, 700, 200, 1000, 600, 500, 450, 270, 350]
c = [138, 328, 281, 165, 550, 350, 303, 220, 330, 280]

data = [[y[i]] + [c[i]] for i in range(min(len(y), len(c)))]
data = pd.DataFrame(data, columns=["Y", "C"])

print("관측값\\n", data)
print("\\n상관계수\\n", data.corr())

결과는 다음과 같이 $2\times2$행렬의 형태로 나온다. 이를 상관행렬(Correlation Matrix; 相关矩阵)이라고 한다.

관측값
       Y    C
0   100  138
1   300  328
2   700  281
3   200  165
4  1000  550
5   600  350
6   500  303
7   450  220
8   270  330
9   350  280

상관계수
           Y         C
Y  1.000000  0.811832
C  0.811832  1.000000

상관행렬의 성분은 모두 상관계수이다. 제1행 제1열의 성분은 1.00인데, 이는 $R(Y,Y)=1$이라는 뜻으로 해석한다. 제2행 제1열의 성분은 0.81인데, 이는 $R(Y,C)=0.81$라는 뜻이다. 상관계수가 0.81이면 두 변수 간 강한 양의 상관관계가 있다는 의미이다. 따라서 ‘한 사람의 한 달 소득과 소비는 강한 양의 상관관계를 가진다’라는 결론을 얻는다.

note1: ‘소득이 많으면 소비도 많이한다’는 식으로 해석할 수는 없다. 소득과 소비 중 어느 것이 원인이고 결과인지 알 수 없기 때문에, ‘소비를 많이 하면 소득도 많다’라고 반대로 해석할 수도 있기 때문이다. 이를 두고 흔히 상관분석(Correlation Analysis; 相关分析)은 상관관계를 발견할 뿐이고 인과관계를 발견할 수 없다고 표현한다. 인과관계는 회귀분석(Regression Analysis; 回归分析)을 통해 발견할 수 있다.

note2: 사실 상관계수를 구하는 것만으로는 완벽한 상관분석이라 할 수 없고, 보통 통계적 유의성(Statistical Significance; 显著性)을 검정하는 과정을 거쳐야 한다. 추후 작성예정.

상관행렬은 항상 주대각선의 성분이 1인 대칭행렬이다. 상관계수의 정의에 의해 임의의 확률변수 $X$와 $Y$에 대해 항상 $R(X,X)=1$이고 $R(X,Y)=R(Y,X)$이 성립하기 때문이다.

위 데이터에 새로운 변수인 연령 $A$를 추가해서 상관계수를 구해보자.

import pandas as pd

y = [100, 300, 700, 200, 1000, 600, 500, 450, 270, 350]
c = [138, 328, 281, 165, 550, 350, 303, 220, 330, 280]
a = [27, 25, 48, 69, 46, 31, 37, 72, 21, 25]

data = [[y[i]] + [c[i]] + [a[i]] for i in range(min(len(y), len(c), len(a)))]
data = pd.DataFrame(data, columns=["Y", "C", "A"])

print("관측값\\n", data)
print("\\n상관계수\\n", data.corr())

결과는 다음과 같다.

관측값
       Y    C   A
0   100  138  27
1   300  328  25
2   700  281  48
3   200  165  69
4  1000  550  46
5   600  350  31
6   500  303  37
7   450  220  72
8   270  330  21
9   350  280  25

상관계수
           Y         C         A
Y  1.000000  0.811832  0.187744
C  0.811832  1.000000 -0.220045
A  0.187744 -0.220045  1.000000

우선, $R(Y,C)=R(C,Y)=0.81$로 같다. 새로운 변수를 추가하더라도 원래의 상관관계에는 영향이 없다. 그리고 $C$와 $A$, $Y$와 $A$모두 상관관계가 약한 것으로 드러났다. 한편, 주대각선의 성분은 모두 1이며, 상관행렬은 대칭행렬이다.

#3. Python으로 상관계수 시각화하기

#3.1 산점도와 회귀선

<hide/>
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 데이터 입력
y = [100, 300, 700, 200, 1000, 600, 500, 450, 270, 350]
c = [138, 328, 281, 165, 550, 350, 303, 220, 330, 280]
a = [27, 25, 48, 69, 46, 31, 37, 72, 21, 25]
variables = [y, c, a]
variables_name = ["Y", "C", "A"]

# 데이터 프레임 생성
data = [[y[i]] + [c[i]] + [a[i]] for i in range(min(len(y), len(c), len(a)))]
data = pd.DataFrame(
    data, columns=[variables_name[i] for i in range(len(variables_name))]
)

# 데이터, 상관계수 출력
print("관측값\n", data)
print("\n상관계수\n", data.corr())

# 3x3 산점도 그리기
fig, ax = plt.subplots(3, 3, layout="constrained")
for i in range(len(variables)):
    for j in range(len(variables)):
        # 산점도 그리기
        ax[i, j].scatter(variables[i], variables[j], color="#000")
        # 회귀선 그리기
        linear_model = np.polyfit(variables[i], variables[j], 1)
        linear_model = np.poly1d(linear_model)
        ax[i, j].plot(variables[i], linear_model(variables[i]), color="r")
        # 상관계수 계산, 제목에 넣기
        r = data[variables_name[i]].corr(data[variables_name[j]])
        title = (
            "R(%s" % variables_name[i]
            + ",%s) =" % variables_name[j]
            + " %s" % round(r, 3)
        )
        ax[i, j].set_title(title)

# 이미지 저장
fig.set_size_inches(10, 10)
fig.savefig("Correlation Scatter Plot")

#3.2 히트맵

<hide/>
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 데이터 입력
y = [100, 300, 700, 200, 1000, 600, 500, 450, 270, 350]
c = [138, 328, 281, 165, 550, 350, 303, 220, 330, 280]
a = [27, 25, 48, 69, 46, 31, 37, 72, 21, 25]
variables_name = ["Y", "C", "A"]

# 데이터 프레임 생성
data = [[y[i]] + [c[i]] + [a[i]] for i in range(min(len(y), len(c), len(a)))]
data = pd.DataFrame(
    data, columns=[variables_name[i] for i in range(len(variables_name))]
)

# 데이터, 상관계수 출력
print("관측값\n", data)
print("\n상관계수\n", data.corr())

# 3x3 히트맵 그리기
plt.figure(figsize=(10, 10))
color = sns.color_palette("Spectral", as_cmap=True)
heatmap = sns.heatmap(
    data.corr(), vmin=-1, vmax=1, annot=True, cmap=color, linewidths=0.5
)

# 이미지 저장
plt.savefig("Correlation Heatmap")

'Mathematics' 카테고리의 다른 글

평균제곱오차와 선형예측  (0) 2023.01.11
이변량 정규분포  (0) 2023.01.09
공분산  (0) 2023.01.01
분산과 표준편차  (0) 2022.12.28
평균과 기댓값  (0) 2022.12.27