본문 바로가기

R/R을 이용한 데이터 처리&분석 실무

분할표

반응형

분할표(Contingency Table)는 명목형(Categorical) 또는 순서형(Ordinal) 데이터의 도수(frequency)를 표 형태로 기록한 것이다. 분할표가 작성되면 카이 제곱 검정(Chi Square Test)으로 변수 간에 의존 관계가 있는지를 독립성 검정으로, 도수가 특정 분포를 따르는지를 적합도 검정(Goodness of Fit)으로 살펴볼 수 있다.

 

분할표가 사용되는 한 가지 경우는 기계 학습으로 데이터의 양성(Positive), 음성(Negative)을 예측할 때다. 예를 들어, 이메일 텍스트를 보고 해당 이메일이 스팸인지 아닌지를 예측하는 경우를 생각해보자. 이때 두 가지 변수는 예측값(모델로 스팸인지를 판단한 결과)과 실제 값(실제로 해당 이메일이 스팸인지 여부)이다. 이런 실험에서 분할표의 모양은 다음과 같다.

  예측-스팸 예측-스팸 아님
실제-스팸 b
실제-스팸 아님 c d

표에서 a는 주어진 이메일이 실제로 스팸일 때 모델의 예측 결과도 스팸인 경우의 수다. b는 실제로 스팸인데 예측은 스팸이 아니라고 된 경우다. c와 d도 유사하게 해석할 수 있다.

 

 

 

분할표 작성

분할표를 작성하는 함수에는 table(), xtabs()가 있다. table()은 "최빈값" 절에서 보인 바 있다. 여기서는 xtabs()를 주로 살펴본다.

 

-xtabs : 포뮬러를 사용해 분할표를 작성한다.

xtabs(

   formula,         # L1 ~ R1 + R2 + R3 형태의 포뮬러다. R1, R2, R3 등은 분할표의 분류를

   # 나타내는 변수들이다. "~"의 왼쪽에 빈도를 나타내는 변수를 적을 수 있다.

   data,             # 포뮬러를 적용할 데이터

)

 

다음은 table()을 사용해 주어진 벡터에서 a, b, c의 출현 횟수를 세는 간단한 예다.

>table(c("a", "b", "b", "b", "c", "c", "d"))
a b c d
1 3 2 1

 

xtabs()는 포뮬러를 사용해 데이터를 지정할 수 있다. 예를 들어, x, y라는 두 변수가 있고 (x, y)에 대한 도수가 num에 저장되어 있을 때, 이 데이터로부터 분할표를 만드는 포뮬러는 num ~ x + y다. 다음은 이 포뮬러를 사용해 분할표를 작성하는 예다.

>d<-data.frame(x=c("1", "2", "2", "1"),
+                 y=c("A", "B", "A", "B"),
+                 num=c(3, 5, 8, 7))

>(xtabs(num ~ x + y, data=d))
   y
x  A B
 1 3 7
 2 8 5

 

만약 도수를 나타내는 컬럼이 따로 없고, 각 관찰 결과가 서로 다른 행으로 표현되어 있다면 ‘~ 변수 + 변수 …’ 형태로 포뮬러를 작성한다. 다음 코드는 x 값이 A 또는 B인 각 경우의 수를 세는 예를 보여준다.

>(d2<-data.frame(x=c("A", "A", "A", "B", "B")))
  x
1 A
2 A
3 A
4 B
5 B

>(xtabs(~ x, d2))
x
A B
3 2

 

 

합, 비율의 계산

여러 변수가 있을 때 한 변수만을 기준으로 총계를 구하는 경우를 생각해보자. 예를 들어, 다음은 변수 A, B에 대한 분할표다. 각 변수는 True, False 값을 가질 수 있다. 이 표에는 변수 B가 True, False인 경우의 합(100과 90)을 표시한 컬럼과, 변수 A가 True, False일 때 합을 표시한 총계 행(80과 110)이 표시되어 있다. 또, 전체 도수의 합 역시 표시되어 있다. 이러한 총계 컬럼 또는 행을 주변 합(marginal sum)이라고 한다. ‘주변’이란 데이터가 표시된 바깥쪽 행 또는 열에 값이 기록되기에 붙여진 이름이다.

  변수A-True 변수A-False 통계
변수B-True 30 70 100
변수B-False 50 40 90
통계 80 110 190

 

주변 합이 계산되고 나면 이를 기준으로 비율을 계산할 수 있다. 예를 들어, 다음은 위에 보인 표에서 변수 B의 합을 기준으로 비율을 계산한 결과다.

  변수A-True 변수A-False
변수B-True 0.3 (= 30/100) 0.7 (= 70/100)
변수B-False 0.56 (= 50/90) 0.44 (= 40/90)

 

마찬가지로 변수 A의 합을 기준으로 비율을 구하거나 전체 데이터의 총 빈도(190)를 기준으로 비율을 계산할 수도 있다.

 

주변 합과 주변 비율은 margin.table(), prop.table()로 계산한다.

 

-margin.table : 분할표의 주변 합을 구한다.

margin.table(

   x,             # 배열

   # 색인 번호. 1은 행 방향, 2는 열 방향을 뜻한다. 기본값인 NULL은 전체 값의 합을 구한다.

   margin=NULL 

)

 

-prop.table : 분할표의 주변 비율을 구한다.

prop.table(

   x,

   margin=NULL

)

 

다음은 margin.table()을 사용해 행 방향, 열 방향의 합과 전체 합을 구한 예다.

>xt<-(xtabs(num ~ x + y, data=d)) 
>xt
   y
x  A B
 1 3 7
 2 8 5

>margin.table(xt, 1)  # 3 + 7 = 10, 8 + 5 = 13
x
 1  2
10 13

>margin.table(xt, 2)  # 3 + 8 = 11, 7 + 5 = 12
y
 A  B
11 12

>margin.table(xt)     # 3 + 7 + 8 + 5 = 23
[1] 23

 

prop.table( )은 분할표로부터 각 셀의 비율을 계산한다. 호출 형식은 margin.table()의 경우와 동일하다.

> xt
  y
x  A B
 1 3 7
 2 8 5

>prop.table(xt, 1)  # xt의 각 행을 각각 10(= 3 + 7), 13(= 8 + 5)로 나눈 값
  y
x          A         B
 1 0.3000000 0.7000000
 2 0.6153846 0.3846154

>prop.table(xt, 2)  # xt의 각 열을 각각 11(= 3 + 8), 12(= 7 + 5)로 나눈 값
  y
x          A         B
 1 0.2727273 0.5833333
 2 0.7272727 0.4166667

>prop.table(xt)     # xt의 각 셀을 전체 데이터의 합 23(= 3 + 7 + 8 + 5)로 나눈 값
  y
x          A         B
 1 0.1304348 0.3043478
 2 0.3478261 0.2173913

 

 

독립성 검정

분할표의 행에 나열된 변수와 열에 나열된 변수가 독립이라고 가정하자. 만약 분할표에서 행과 열이 독립이라면 (i, j) 셀의 확률 P(i, j)에 대해 다음 식이 성립한다.

식 7-1

예를 들어, 다음 분할표를 생각해보자.

  변수A-True 변수A-False 통계
변수B-True 30 70 100
변수B-False 50 40 90
통계 80 110 190

 

표에서 변수 A가 True일 확률은 80/190, 변수 A가 False일 확률은 70/190이다. 마찬가지로 변수 B가 True일 확률은 100/190, 변수 B가 False일 확률은 90/190이다. 따라서 만약 A와 B가 독립이라면 변수 A가 True이고, 변수 B가 True일 확률은 (80/190) * (100/190)이라고 할 수 있다. 독립성 검정(Independence Test)은 실제로 이와 같은 가정이 성립하는지 알아보는 것을 목표로 한다.

 

변수 간의 독립성 검정에는 카이 제곱 검정(Chi-Squared Test)을 사용하며, 이때 사용되는 통계량은 다음과 같다.

식 7-2

위 식에서 r은 행의 수, c는 열의 수를 의미한다. Oij는 분할표의 (i, j) 셀에 기록되어 있는 값이며, 분할표를 보면 바로 알 수 있는 값이다. Eij는 분할표의 두 변수가 독립일 때 (i, j) 셀에 대한 기댓값이다. 식 7-1에 의해 전체 데이터의 수가 n이라 할 때 Eij = n×P(i, j) = n×P(i)×P(j)다.

 

학생 설문 조사 데이터를 담고 있는 MASS::survey를 사용해 학생들의 성별에 따른 운동량에 차이가 있는지 독립성 검정을 해보자. 다음은 survey 데이터의 모양을 보여준다.

>library(MASS)
>data(survey)
>str(survey)
'data.frame':   237 obs. of 12 variables:
$ Sex   : Factor w/ 2 levels "Female","Male": 1 2 2 2 2 1 2 1 2 2 ...
$ Wr.Hnd: num 18.5 19.5 18 18.8 20 18 17.7 17 20 18.5 ...
$ NW.Hnd: num 18 20.5 13.3 18.9 20 17.7 17.7 17.3 19.5 18.5 ...
$ W.Hnd : Factor w/ 2 levels "Left","Right": 2 1 2 2 2 2 2 2 2 2 ...
$ Fold  : Factor w/ 3 levels "L on R","Neither",..: 3 3 1 3 2 1 1 3 3 3 ...
$ Pulse : int 92 104 87 NA 35 64 83 74 72 90 ...
$ Clap  : Factor w/ 3 levels "Left","Neither",..: 1 1 2 2 3 3 3 3 3 3 ...
$ Exer  : Factor w/ 3 levels "Freq","None",..: 3 2 2 2 3 3 1 1 3 3 ...
$ Smoke : Factor w/ 4 levels "Heavy","Never",..: 2 4 3 2 2 2 2 2 2 2 ...
$ Height: num 173 178 NA 160 165 ...
$ M.I   : Factor w/ 2 levels "Imperial","Metric": 2 1 NA 2 2 1 1 2 2 2 ...
$ Age   : num 18.2 17.6 16.9 20.3 23.7 ...

>head(survey[c("Sex", "Exer")])
     Sex Exer
1 Female Some
2   Male None
3   Male None
4   Male None
5   Male Some
6 Female Some

 

survey 데이터에서 성별은 Sex, 운동을 얼마나 하는지는 Exer 열에 저장되어 있다. Sex는 Female, Male 두 가지 레벨을 가지는 팩터이며, Exer는 Freq, Some, None 3가지 레벨로 구성된 팩터이다. Freq는 운동을 자주함, Some은 운동을 약간 함, None은 운동을 하지 않음을 의미한다.

 

성별과 운동이 독립인지를 확인해보기 위해 분할표를 만들어보자.

>xtabs(~ Sex + Exer, data=survey)
       Exer
Sex     Freq None Some
  Female   49   11   58
  Male     65   13   40

 

분할표를 작성하고 나면 chisq.test()를 통해 카이 제곱 검정을 수행할 수 있다.

 

-chisq.test : 카이 제곱 검정을 수행한다.

chisq.test(

   x,               # 숫자 벡터 또는 행렬. 또는 x y가 모두 팩터

   y=NULL,      # 숫자 벡터 또는 x가 팩터인 경우 팩터로 지정. x가 행렬인 경우 그 안에 분할표가

                    # 저장되어 있는 경우므로 y가 무시된다.

   # x와 같은 길이를 가질 확률. 이 값이 비율이 이 확률과 같은지를 테스트한다. 이 값이 지정되지

   # 않으면 확률이 서로 같은지 테스트한다. 이 인자는 '7.5 적합도 검정' 절에서 사용법을 설명한다.

   p = rep(1/length(x), length(x))

)

 

다음은 성별과 운동 정도의 독립성 검정을 수행한 예다.

>chisq.test(xtabs(~ Sex + Exer, data=survey))

  Pearson's Chi-squared test

data: xtabs(~Sex + Exer, data = survey)
X-squared = 5.7184, df = 2, p-value = 0.05731

 

p 값이 0.05731이므로 0.05보다 커서 ‘H0: 성별과 운동은 독립이다.’라는 귀무가설을 기각할 수 없는 것으로 나타났다. 통계량 χ2은 5.7184였으며 자유도(Degree of Freedom)는 성별이 2개 레벨, 운동량이 3개 레벨이므로 (2-1)(3-1) = 2였다.

 

 

 

피셔의 정확 검정

분할표를 그린 뒤 카이 제곱을 적용할 때 표본 수가 적거나 표본이 분할표의 셀에 매우 치우치게 분포되어 있다면 카이 제곱 검정의 결과가 부정확할 수 있다. 표본 수가 적다는 기준은 특정 짓기 어렵지만 기대 빈도가 5 이하인 셀이 전체의 20% 이상인 경우 등이 이에 해당한다. chisq.test()는 이런 경우 경고 메시지를 출력하여 카이 제곱 검정이 부정확할 수 있음을 알린다.

 

카이 제곱 검정이 부정확한 경우에는 피셔의 정확 검정(Fisher’s Exact Test)을 사용한다. 통계적인 계산식에 대한 설명은 피셔의 정확한 검정에 대한 참고자료를 보기 바란다. 이 절에서는 R에서 피셔의 정확 검정을 수행하는 방법에 대해 설명할 것이다. 피셔의 정확 검정에는 fisher.test() 함수를 사용한다.

 

-fisher.test : 피셔의 정확 검정을 수행한다.

fisher.test(

   x,             # 행렬 형태의 이차원 분할표 또는 팩터

   y=NULL,    # 팩터. x가 행렬이면 무시된다. alternative="two.sided"

   # 대립가설로 two.sided는 양측 검정, less는 작다, greater는 크다를 의미

)

 

MASS::survey 데이터에서 손 글씨를 어느 손으로 쓰는지와 박수를 칠 때 어느 손이 위로 가는지 사이의 경우에 대해 피셔의 정확 검정을 수행해보자. 분할표를 xtab()으로 구한 뒤 카이 제곱 검정을 수행하면 카이 제곱 검정이 정확하지 않다는 경고 메시지가 나온다.

>xtabs(~ W.Hnd + Clap, data=survey)
      Clap
W.Hnd  Left Neither Right
  Left    9       5     4
  Right  29      45   143

>chisq.test(xtabs(~ W.Hnd + Clap, data=survey))

  Pearson's Chi-squared test

data:   xtabs(~ W.Hnd + Clap, data = survey)
X-squared = 19.2524, df = 2, p-value = 6.598e-05

Warning message:
In chisq.test(xtabs(~ W.Hnd + Clap, data = survey)) :
  Chi-squared approximation may be incorrect

 

이 경우 fisher.test() 함수를 사용해야 한다.

>fisher.test(xtabs(~ W.Hnd + Clap, data=survey))

  Fisher's Exact Test for Count Data

data: xtabs(~W.Hnd + Clap, data = survey)
p-value = 0.0001413
alternative hypothesis: two.sided

 

p-value가 0.05보다 작으므로 글씨를 쓰는 손과 박수를 칠 때 위에 오는 손이 독립이라는 귀무가설을 기각하고 둘 사이에 관계가 있다는 대립가설을 채택한다.

 

 

 

맥니마 검정

벌금을 부과하기 시작한 후 안전벨트 착용자의 수, 선거 유세를 하고 난 뒤 지지율의 변화와 같이 응답자의 성향이 사건 전후에 어떻게 달라지는지를 알아보는 경우 맥니마 검정McNemar Test을 수행한다. 사건 전후에 응답자에게 설문을 하여 사건 발생 전 설문 결과를 Test1, 사건 발생 후 설문 결과를 Test2라고 명시한 다음을 보자.

  Test 2 양성(positive) Test 2 음성(negative) 총계
Test 1 양성(positive) a b a+b
Test 1 음성(negative) c d c+d
총계 a+c b+d n

 

사건 전후에 설문 결과에 응답자 수 변화가 없다면 Test1의 positive와 Test2의 positive가 동일해야 하므로 a + b = a + c가 성립해야 한다. 또한, Test1의 negative와 Test2의 negative가 동일해야 하므로 c + d = b + d가 성립해야 한다. 이 둘을 정리해 결과적으로 b = c 여부를 검토하면 사건 전후에 성향 변화가 생겼는지 알 수 있다.

 

b = c가 성립하려면 b, c의 값이 b + c의 절반씩이 되어야 하므로 b는 이항 분포를 따른다.

이항 분포 B(n, p)에서 n(앞서 설문 조사 표에서는 b + c에 해당)이 크다면 이항 분포를 정규 분포로 근사할 수 있다.

 

b를 표준화하여 N(0, 1)을 따르게 하고 연속성 수정(Continuity Correction)을 하면 다음이 성립한다.

 

이제 ~의 좌측에 있는 통계량을 계산한 다음 이 값이 χ2(1)의 어디에 있는지를 보면 b=c인지 여부를 알 수 있다. 이를 수행해주는 R 함수는 mcnemar.test()다. 또는 b가 b + c의 절반에 해당하는지를 이항 분포를 사용해 검정할 수도 있다. 이때 사용하는 함수는 binom.test()다.

 

-mcnemar.test : 맥니마 검정을 수행한다.

mcnemar.test(

   x,                    # 행렬 형태의 이차원 분할표 또는 팩터

   y=NULL,           # 팩터. x가 행렬일 경우 무시

   correct=TRUE    # 연속성 수정 적용 여부

)

 

-binom.test : 이항 분포 검정을 수행한다.

binom.test(

   x,               # 성공의 수. 또는 성공과 실패 수를 각각 저장한 길이 2인 벡터

   n,               # 시행 횟수. x의 길이가 2라면 무시

   p=0.5,         # 성공 확률에 대한 가설

   alternative=c("two.sided", "less", "greater") # 대립 가설의 형태. 기본값은 양측 검정

)

 

다음은 help(mcnemar.test)에서 가져온 것으로, 2×2 분할표에서 맥니마 검정을 사용하는 예다. 사용된 데이터는 투표권이 있는 나이의 미국인 1,600명에 대해 대통령 지지율을 조사한 것으로, 1차 조사(1st Survey)와 2차 조사(2nd Survey)는 한 달 간격으로 수행되었다.

>## Agresti (1990), p. 350.
>## Presidential Approval Ratings.
>## Approval of the President's performance in office in two
>## surveys, one month apart, for a random sample of 1600
>## voting-age Americans.
>Performance<-
+   matrix(c(794, 86, 150, 570),
+          nrow = 2,
+          dimnames = list(
+             "1st Survey" = c("Approve", "Disapprove"),
+             "2nd Survey" = c("Approve", "Disapprove")))

>Performance
          2nd Survey
1st Survey  Approve Disapprove
  Approve       794        150
  Disapprove     86        570

>mcnemar.test(Performance)

   McNemar's Chi-squared test with continuity correction

data: Performance
McNemar's chi-squared = 16.8178, df = 1, p-value = 4.115e-05

 

과에서 p-value < 0.05가 나타나 사건 전후에 Approve, Disapprove에 차이가 없다는 귀무가설이 기각된다. 즉, 사건 전후에 Approve, Disapprove 비율에 차이가 발생했다.

 

앞서 mcnear.test()는 이항 분포로부터 나왔으며, b가 b + c의 절반에 해당하는지를 보는 것이라고 설명했다. 따라서 binom.test()를 사용해 1차 조사에서의 Disapprove와 2차 조사에서의 Disapprove가 같은 값인지 확인할 수 있다. 다음 코드에서는 86이 86 + 150의 절반에 해당하는지를 검정하고 있다.

>binom.test(86, 86 + 150, .5)

  Exact binomial test

data: 86 and 86 + 150
number of successes = 86, number of trials = 236, p-value = 3.716e-05
alternative hypothesis: true probability of success is not equal to .5
95 percent confidence interval:
 0.3029404 0.4293268
sample estimates:
probability of success
             0.3644068

 

여기에서도 p-value < 0.05로 나타나 86이 86 + 150의 절반이라는 귀무가설이 기각되었다. 즉, 사건 전후에 Approve, Disapprove 성향 차이가 발생했다.

 

 

R을 이용한 데이터 처리&분석 실무 中

반응형

'R > R을 이용한 데이터 처리&분석 실무' 카테고리의 다른 글

선형 회귀-1  (0) 2020.02.06
상관 분석  (0) 2020.02.06
표본 추출  (0) 2020.02.05
기초 통계량  (0) 2020.02.04
난수 생성 및 분포 함수  (0) 2020.02.04