상관분석(Correlation analysis)
상관분석은 두 개 이상의 자료에 대한 상관 관계를 분석하는 것으로 분석의 모수는 상관계수(ρ)가 됩니다. 식 1에서 나타낸 것과 같이 분석의 귀무가설은 ρ = 0입니다. 다시 말해 비교하는 자료들 사이의 상관성은 존재하지 않음을 검정하는 것입니다.
H0 : ρ = 0, H1 : ρ ≠ 0 | (식 1) |
일반적으로 상관계수는 ρ 또는 r로 나타냅니다.
상관계수(r)에 대한 분포는 평균 0이며 범위는 [-1, 1] 이므로 그 분포의 분산은 1 - r2 로 나타낼 수 있습니다. 이 확률변수는 경계값과 평균이 고정되므로 자유도는 n - 2인 t분포를 따릅니다. 그러나 자유도가 클 경우는 정규분포를 적용합니다. 확률변수의 표준오차와 검정통계량은 식 2로 계산됩니다.
\begin{align}\text{SE}&=\sqrt{\frac{1-r^2}{n-2}}\\ \text{statistic}& = \frac{r-\mu_r}{\sqrt{\frac{1-r^2}{n-2}}}\\ & = \frac{r}{\sqrt{\frac{1-r^2}{n-2}}}\\ \text{SE}:&\,\text{표준오차}\end{align} | (식 2) |
식 2에서의 검정통계량은 상관계수를 표준화한 것으로 표준 정규분포 또는 표준 t 분포를 기반으로 검정합니다. 검정에 t-분포 또는 정규분포를 적용할 경우 통계량은 상관계수가 됩니다.
예 1)
일정기간의 kos와 ex의 일일 종가에 대한 상관분석을 실시합니다.
ex | kos | |
---|---|---|
0 | 1260.91 | 2218.68 |
1 | 1270.10 | 2255.98 |
2 | 1279.08 | 2264.65 |
3 | 1271.08 | 2289.97 |
4 | 1274.18 | 2350.19 |
5 | 1252.88 | 2351.31 |
다음은 위 자료를 생성하기 위한 코드입니다.
st=pd.Timestamp(2023,1,1) et=pd.Timestamp(2024, 5, 30) kos=fdr.DataReader('KS11', st, et)["Close"] ex=fdr.DataReader('USD/KRW',st, et)["Close"] data=pd.concat([ex, kos.shift(periods=-1)], join="inner", axis=1) data.index=range(len(data)) data.columns=['ex', 'kos'] data=data.dropna()
자료의 ex와 kos간의 스케일차이를 조정하기 위해 표준화를 실시합니다. sklearn.preprocessing.StandardScaler() 함수를 적용합니다.
from sklearn.preprocessing import StandardScaler
scaler=StandardScaler().fit(data) da=scaler.transform(data) print(np.around(da[:3, :], 3))
[[-1.666 -2.981] [-1.385 -2.637] [-1.112 -2.557]]
객체 da의 두 변수에 대한 상관 행렬은 np.corrcoef() 메서드를 적용합니다.
corMat=np.corrcoef(da, rowvar=False) print(np.around(corMat, 3))
[[1. 0.357] [0.357 1. ]]
검정 통계량에 대한 z 검정을 실시합니다. 귀무가설의 상관계수가 0이므로 평균 0, 표준오차에 대한 정규분포를 기준으로 p-value를 계산하면 다음과 같습니다.
r=corMat[1,0] df=len(da)-2 se=np.sqrt((1-r**2)/df) print(f"표준오차: {round(se, 3)}, static: {round(r, 3)}")
표준오차: 0.051, static: 0.357
pValue=2*stats.norm.cdf(r, 0, se) print("p-value: %.3f" %pValue)
p-value: 0.000
위 결과인 유의확률(p-value)은 상관계수의 평균 0, 표준오차 se의 분포 상에서 매우 극단적인 경우로 귀무가설 r = 0을 채택할 수 없음을 나타냅니다. 즉, 두 자료의 상관성은 존재한다고 할 수 있습니다. 이 결과는 그림 1로 명확해집니다.
fig, ax=plt.subplots(figsize=(4,3)) ax.scatter(da[:,0], da[:,1], s=10, color="green") ax.spines["left"].set_position("center") ax.spines["right"].set_visible(False) ax.spines["top"].set_visible(False) ax.spines["bottom"].set_position(("data",0)) ax.set_xlabel("ue(%)", fontsize=13, fontweight="bold") ax.set_ylabel("vix(%)", fontsize=13, fontweight="bold") ax.xaxis.set_label_coords(0.9, 0.45) ax.yaxis.set_label_coords(0.55, 0.9) plt.show()
신뢰구간은 추정값이 포함될 수 있는 확률변수의 범위입니다. 상관계수의 경우는 [-1, 1]사이의 값들로서 정규분포나 t분포의 확률변수를 그대로 적용할 수 없습니다. 그러므로 상관계수는 Fisher 변환방법으로 변환한 후 다음 방법으로 신뢰구간을 결정합니다.
- Fisher 변환으로 확률변수 변환
- $z_r=\frac{\ln\left(\frac{1+r}{1-r}\right)}{2}$
- 변환된 확률변수의 표준편차
- $s=\frac{1}{\sqrt{n-3}}$
- 정규분포의 통계량을 사용하여 신뢰구간을 계산 (신뢰구간 하한: L, 상한: U)
- $\text{신뢰구간}=z_r \pm Z_{1-\frac{\alpha}{2}}\cdot s$
- 위의 신뢰구간을 상관계수의 스케일로 환원
- $\begin{align}\text{하한}&=\frac{e^{2L}-1}{e^{2L}+1}\\\text{상한}&=\frac{e^{2U}-1}{e^{2U}+1}\end{align}$
z=np.log((1+r)/(1-r))/2 round(z, 3)
0.374
n=len(data) L, U=z-1.96/np.sqrt(n-3), z+1.96/np.sqrt(n-3) print('하한: %.3f, 상한: %.3f' %(L, U))
하한: 0.267, 상한: 0.480
위 결과는 변환된 스케일로 상관계수의 범위인 [-1,1]의 스케일로 환원이 필요합니다.
L1,U1=(np.exp(2*L)-1)/(np.exp(2*L)+1), (np.exp(2*U)-1)/(np.exp(2*U)+1) print('신뢰하한: %.3f, 신뢰상한: %.3f' %(L1, U1))
신뢰하한: 0.261, 신뢰상한: 0.446
위 결과 신뢰구간은 0을 포함하지 않으며 -1의 방향으로 치우져 있습니다. 그러므로 상관계수 = 0 이라는 귀무가설을 기각할 수 있으며 두변수는 강한 역관계에 있음을 나타냅니다.
위 분석은 stats.pearsonr(x, y) 함수를 사용하여 중간 과정없이 분석 결과를 확인할 수 있습니다. 이 함수의 결과 중 신뢰구간은 .confidence_interval(confidence_level=0.95)
메소드로 확인할 수 있습니다.
re=stats.pearsonr(da[:,0], da[:,1]) print(f"statistic: {re[0].round(3)}, p-value: {re[1].round(3)}")
statistic: 0.357, p-value: 0.0
ci=re.confidence_interval(0.95) print(f"신뢰하한: {ci[0].round(3)}, 신뢰상한: {ci[1].round(3)}")
신뢰하한: 0.261, 신뢰상한: 0.446
댓글
댓글 쓰기