소벨 테스트 완전히 이해하기 (3): 다변량 델타 메쏘드와 그 적용


이제 드디어 소벨 테스트를 직접 유도하기 직전까지 왔습니다. 혹시 델타 메쏘드에 대한 이전 글을 보지 못하신 분이 계시다면 먼저 읽고 오시는 것을 추천드립니다. 그럴 시간적 여유가 없으신 분들을 위해 다시 한 번 요약하자면, 델타 메쏘드는

1) 모수치를 변환한 값에 대한 추정치와 그 근사적 정규분포를 구하는 방법이다.

2) 특정 조건 하에, 변환된 모수치에 대해 추정할 때도 원래 모수치에 대한 추정치를 그대로 변환 공식에 대입할 수 있다.

3) 단, 분산은 변화하고, 그 비율은 변환을 미분한 것에 참값을 대입한 것의 제곱과 같다.

정도로 정리하겠습니다.

이제 드디어 단순매개분석의 경우를 고려합니다. 여기서 신뢰구간을 구하려는 대상은 두 경로계수의 곱, 즉 \(ab\) 였습니다. 다시 한 번 상기하자면, \(a\)는 독립변수 \(X\)에서 매개변수 \(M\)으로 가는 경로의 회귀계수, \(b\)는 매개변수 \(M\)에서 종속변수 \(Y\)로 가는 경로의 회귀계수였습니다. 그리고 \(ab\)가 유의하게 0과 다른지, 즉 그 신뢰구간이 0을 포함하는지 아닌지 보려는 것이 “product of coefficients” 방식 매개분석의 목적이라고 했습니다.

이제 델타 메쏘드를 적용할 준비를 하겠습니다. 일단 세 가지 요소가 필요합니다:

1) 변환에 사용된 함수를 “미분”한 것, 즉 그래디언트 gradient

2) 원 추정치의 분산, 즉 공분산행렬 covariance matrix

3) 1)의 “제곱”을 2)에 곱하는 절차, 즉 이차형식 quadratic form

수학 용어가 등장한다고 지레 겁먹지 마시기 바랍니다. 알고보면 별로 안 어렵습니다. 이제 하나씩 살펴보겠습니다.

입력값이 여럿인 함수를 “미분”하는 것은 사실 하나도 어려울 게 없습니다. 개별 입력값에 대해 하나씩 차례대로 미분하면 됩니다. 그 결과들을 하나씩 원소로 넣은 벡터를 “그래디언트” gradient 라고 부릅니다. 그래디언트는 변수가 하나인 경우의 한 번 미분한 함수, 즉 \(f’(x)\)와 개념적으로는 완전히 동일합니다. 표기법만 쬐끔 다를 뿐이죠. \(\nabla f\)라고 표기합니다. \(f\big((a,b)^T\big)=ab\)에 대한 그래디언트를 구하는 것은 더더욱 쉽습니다. 고교 문과 수준의 미분만 할 줄 알면 됩니다. \(ab\)를 \(a\)에 대해 미분한 것은 뭐죠? \(b\)입니다. \(b\)에 대해 미분한 건 그럼 뭐죠? \(a\)입니다. 그래서 그래디언트는 그냥 \(\nabla f\big((a,b)^T\big) = (b,a)^T\) 입니다. 끝. 간단하죠? 전혀 겁먹을 필요가 없습니다. 실제로는 우리는 \(a\)와 \(b\)의 값을 알지 못하니 추정치를 넣는데, 이건 단순매개분석의 두 회귀식 (\(M\) ~ \(X\), \(Y\) ~ \(M\) + \(X\)) 을 돌려 얻은 값을 그냥 넣어주면 됩니다. 끝.

이제 추정치들의 공분산행렬을 찾아야 되는데, 이건 회귀분석 결과에서 바로 얻을 수 있습니다. R은 vcov() 함수에 회귀분석 결과를 저장한 lm() 오브젝트를 넣으면 추정치들의 분산을 바로 출력해 줍니다. 그리고 하나 중요한 것. 여기서 \(a\)와 \(b\)의 추정치들은 서로 상관이 없다고 가정됩니다 (상관이 있다고 가정하는 버전도 있긴 합니다만 [1]). 상관이 없는 두 변수 사이의 공분산은 물론 0입니다. 그래서 공분산행렬은 대각에 회귀계수 추정치들의 분산, 즉 \(\hat{\sigma}^2_a\)와 \(\hat{\sigma}^2_b\), 그리고 off-diagonal 에는 0이 위치한 2 by 2 행렬이 됩니다.

마지막으로 “제곱”을 하는 것에 대해 이야기하자면, 열벡터 \(x\)의 “제곱”을 행렬 \(A\)에 곱하는 것은 행렬대수에서는 다음과 같이 정의합니다: \(x^TAx\) . 소벨 테스트의 맥락에서 \(x\)에 해당되는 것은 앞에서 구한 그래디언트, \(A\)에 해당되는 것은 공분산행렬입니다. 이제 결과를 구해 보면 다음과 같습니다 (행렬 곱셈은 하실 줄…아시죠?).

마지막 항을 보면 \(\hat{a}\hat{b}\)의 분산은 결국 \(\hat{a}\), \(\hat{b}\) 의 제곱에 서로의 분산을 교차하여 곱해 더한 것과 같다는 사실을 알게 됩니다.

이제 예제를 봅시다. 전체 R 코드

여기서는 우리가 공식을 이용해 얻은 계산 결과가 실제 계산기와 일치하는지 확인하는 것만이 목적이므로 아무 자료나 쓰겠습니다. (또!) iris 자료를 사용합니다. 1열을 \(X\), 2열을 \(M\), 3열을 \(Y\)라고 칩시다. 여기서 \(a\)와 \(b\)의 추정치들을 얻으면 다음과 같습니다:

X <- iris[,1]
M <- iris[,2]
Y <- iris[,3]

fit1 <- lm(M ~ X)
fit2 <- lm(Y ~ X + M)

a_hat <- coef(fit1)[2]
b_hat <- coef(fit2)[3]

a_hat

[1] -0.0618848 

b_hat
         
[1] -1.338623 

이 둘을 곱하면 \(ab\)에 대한 추정치 \(\hat{a}\hat{b}\)를 얻습니다:

mean <- a_hat*b_hat
mean
         
[1] 0.08284043 

이제 공분산행렬에서 각각의 분산을 추출할 차례입니다:

sigma2_a_hat <- vcov(fit1)[2,2]
sigma2_b_hat <- vcov(fit2)[3,3]

sigma2_a_hat

[1] 0.001846162

sigma2_b_hat

[1] 0.01497142

이제 (1) 에 구한 숫자들을 대입해서 \(\hat{a}\hat{b}\)의 표준오차를 계산해 봅시다:

se_ab <- sqrt(a_hat^2*sigma2_b_hat + b_hat^2*sigma2_a_hat)
se_ab

[1] 0.0580129 

이제 추정치와 표준오차까지 얻었으니 \(ab\)의 95% 신뢰구간을 구해 봅시다:

c(mean-1.96*se_ab, mean+1.96*se_ab)

[1] -0.03086486  0.19654572 

\(ab\)에 대한 (근사적) 95% 신뢰구간은 [-0.03, 0.20] 입니다. 이 구간은 0을 포함하므로, 우리는 \(\hat{a}\hat{b}\)로 계량화된 간접효과가 유의하지 않다고 결론내릴 것입니다.

이제 이 결과가 이미 널리 쓰이는 계산기가 산출하는 것과 같은지 보겠습니다. R의 multilevel 패키지는 sobel()이라는, 이름 그대로의 간단한 소벨 테스트를 위한 함수를 제공합니다:

library(multilevel)
fit <- sobel(X, M, Y)

fit$Indirect.Effect
[1] 0.08284043

fit$SE
[1] 0.0580129

c(fit$Indirect.Effect-1.96*fit$SE, fit$Indirect.Effect+1.96*fit$SE)
[1] -0.03086486  0.19654572

결과는 우리가 구한 것과 정확히 일치합니다. 이 외에도 웹 기반 소벨 테스트 계산기 [2] 가 있습니다.

지금까지 소벨 테스트에 델타 메쏘드가 어떻게 사용되는지 알아보고, 실제로 구현까지 해 보았습니다. 따라오신 분들은 어? 생각보다 쉽네? 하고 느끼실지도 모르겠습니다. 예, 사실 그리 어렵지 않습니다. 수식에 지레 겁먹지만 않는다면요. 물론 복잡한 수식이 화려하게 전개되는 방법론들도 많지만, 소벨 테스트는 매우 쉽게 이해 및 구현할 수 있습니다.

지금까지의 튜토리얼을 잘 따라오신 분들이라면 이제 소벨 테스트가 여러 가지 가정에 기반을 두고 있음을 아셨을 것입니다. 첫째, \(\hat{a}\hat{b}\)가 근사적으로 정규분포를 따른다고 가정했습니다. 둘째, \(\hat{a}\)와 \(\hat{b}\)는 상관이 0이라고 가정했습니다. 하지만 현실에서 이런 가정들이 깨지는 경우는 쉽게 발생할 것이라는 점은 명백하죠? 이런 경우 Sobel test가 제공하는 신뢰구간은 믿을만하지 않습니다. 그리고 이것은 오랜 시간동안 소벨 테스트에 대한 강한 비판 지점이 되어 왔습니다. 그러다가 2004년에 와서 부트스트랩 bootstrap 을 이용한 신뢰구간 추정 방식이 적용되면서 소벨 테스트의 이런 약점들을 해결할 대안으로 주목받게 됩니다. 그런데 여기서 놀라운 비밀이 등장합니다. 사실 부트스트랩 방식으로 \(ab\)의 신뢰구간을 만드는 것은 심지어 이론적으로는 소벨 테스트보다 직관적으로 이해하기 더 쉽습니다! 구현도 간단한 편입니다. 그래서 다음 글부터는 부트스트랩의 기본 아이디어부터 시작해서 구현까지 하는 것을 목표로 해 보겠습니다.

[1] 다음 논문 의 85쪽 참고:

MacKinnon, D. P., Lockwood, C. M., Hoffman, J. M., West, S. G., & Sheets, V. (2002). A comparison of methods to test mediation and other intervening variable effects. Psychological Methods, 7(1), 83-104.

[2] 계산기 링크