반응형

[R] 도수분포표 (Frequency table), 분할표 (Contingency table) 만들기 - table(), prop.table(), xtabs(), margin.table()

 

 수천 명의 정보를 포함한 데이터를 한눈에 요약하고 싶을 때가 많다. 나이, 혈압과 같은 연속형 변수는 평균으로 요약하곤 하는데, 성별이나 음주 여부는 평균을 구할 수 없으니 빈도를 제시하곤 한다. 이를 표로 제시하면 도수분포표 (Frequency table)가 된다. 이를 넘어서 남성 중 음주자가 몇 명인지, 여성중 비음주자가 몇 명인지 알고 싶을 때가 있는데, 이때 사용하는 것이 분할표 (Contingency table)이다. 즉 본 글의 목적은 다음 두 개의 표 내용을 채우는 것이다.

 

<도수분포표>

  빈도 백분율 누적빈도 누적백분율
여성        
남성        

 

<분할표>

  비음주자 음주자 합계
여성      
남성      
합계      

 

R이 웬만한 프로그램보다 거의 모든 분야에서 분석하기엔 훨씬 편리하다. 하지만 오늘 다룰 도수분포표 및 분할표는 R이 압도적으로 불편하다. 그 불편한 분석을 오늘 해보고자 한다. (이런 이유로 CrossTable이라는 훌륭한 함수가 개발된 듯 하다. 다음 글로 소개하겠다. -2022.08.31 - [기술 통계/R] - [R] 도수분포표 (Frequency table), 분할표 (Contingency table) 만들기 - CrossTable())

 

*실습용 데이터는 아래 링크를 클릭하면 다운로드할 수 있습니다.

2022.08.04 - [공지사항 및 소개] - 분석용 데이터 (update 22.08.29)

 

분석용 데이터 (update 22.08.29)

2022년 08월 29일 버전입니다. 변수는 계속하여 추가될 예정입니다. 다음 카테고리에 있는 글에서 이용된 데이터입니다. - 기술 통계 - 통계 프로그램 사용 방법 1) 엑셀 파일 2) CSV 파일 3) 코드북

medistat.tistory.com

 

코드를 보여드리기에 앞서 워킹 디렉토리부터 지정하겠다.

워킹 디렉토리에 관한 설명은 다음 링크된 포스트에서 볼 수 있다.

2022.08.05 - [통계 프로그램 사용 방법/R] - [R] 작업 디렉토리 (Working Directory) 지정 - getwd(), setwd()

setwd("C:/Users/user/Documents/Tistory_blog")

 

데이터를 불러와 df에 객체로 저장하겠다.

데이터 불러오는 방법은 다음 링크에서 볼 수 있다.

2022.08.05 - [통계 프로그램 사용 방법/R] - [R] 데이터 불러오기 : EXCEL - read_excel(), read.xlsx()

2022.08.10 - [통계 프로그램 사용 방법/R] - [R] 데이터 저장하기 : CSV 파일 - write.csv(), write_csv()

2022.08.10 - [통계 프로그램 사용 방법/R] - [R] 데이터 불러오기 : SAS file (.sas7bdat) - read.sas7bdat(), read_sas()

install.packages("readr")
library("readr")
df<-read_csv("Data.csv")

 

도수분포표

코드 - table() 

* 변수: SEX (성별)

  -0: 여성

  -1: 남성

table(df$SEX)

table(df$SEX) :df데이터에 있는 SEX변수로 도수분포표를 만들어라.

 

결과

  0   1 
482 518

 

해석

SEX(성별)이 0(여성)인 사람이 482명, 1(남성)인 사람이 518명이다.

 

하지만 앞으로 table()함수는 사용하지 않을 것이다. 왜냐하면, 보면 알겠지만 어떤 변수를 분석한 것인지 표시되지 않는다. 한 개의 변수를 분석할 때에는 문제가 되지 않겠지만 당장 분석하게 될 분할표에서는 2개 이상의 변수가 들어가는데, 어떤 변수가 행에 들어갔는지, 열에 들어갔는지 표시되지 않으므로 헷갈릴 때가 있기 때문이다.

 

코드 - xtabs() 

xtabs(~SEX, data=df)

xtabs(~SEX, data=df) : df라는 데이터에 있는 SEX로 도수분포표를 만들어라. 단, 도수는 정해져 있지 않다. 

 

도수란 무엇인가?

만약 데이터가 다음과 같다면 도수가 있는 데이터라고 할 수 있다.

SEX(성별) ALCOHOL(음주) N(도수=몇 명인데?)
0 1 10
0 0 20
1 1 20
1 0 50

이 데이터는

여성 & 음주자: 10명

여성 & 비음주자: 20명

남성 & 음주자: 20명

남성 & 비음주자: 50명

임을 의미한다. 즉 각 특성을 갖는 사람이 몇 명인지 알려주는 것인데 이런 변수가 있다면 코드 xtabs(~SEX, data=df) ~왼쪽에 N을 적어야 한다.

 

#만약 도수를 나타내는 변수가 있는 자료였다면?
xtabs(N~SEX, data=df)

위 코드는 가상의 코드임으로 구동되지 않는다.

 

결과

SEX
  0   1 
482 518

함수 xtabs()는 정말 고맙게도 변수의 이름을 표기해준다.

 

표의 다음 파란 글씨를 이제 채울 수 있게 되었다.

  빈도 백분율 누적빈도 누적백분율
여성 482      
남성 518      

 

코드 - 분율

분율을 구하는 법은 다음과 같다.

prop.table(xtabs(~SEX, data=df))

prop.table(xtabs(~SEX, data=df)) :위에서 구한 도수분포표 [코드: xtabs(~SEX, data=df)]의 분율(proportion)을 구하라.

 

결과

SEX
    0     1 
0.482 0.518

SEX=0의 분율은 0.482, SEX=1의 분율은 0.518이다. 

하지만, %로 나타난 백분율이 아니므로 이대로는 표에 넣을 수 없다. 그리고 이런 식의 코드는 복잡함을 증대시키기 때문에 보통 다음과 같은 코드를 사용한다. 순서대로 설명하면 다음과 같다.

 

코드 - 백분율

freq_sex<-xtabs(~SEX, data=df)
prop_sex<-prop.table(freq_sex)
prop_sex_100<-100*prop_sex
prop_sex_100
round(prop_sex_100,digits=0)

freq_sex<-xtabs(~SEX, data=df) : 데이터 df안에 있는 변수 SEX의 도수분포표를 작성하여 freq_sex에 저장하라.

prop_sex<-prop.table(freq_sex)  : freq_sex의 분율을 구하여 prop_sex에 저장하라.
prop_sex_100<-100*prop_sex : prop_sex에 100을 곱하여 prop_sex_100에 저장하라

prop_sex_100 : prop_sex_100가 뭔지 보여달라.
round(prop_sex_100,digits=0) : prop_sex_100을 소수점 1번째 자리에서 반올림하여 정수로 보여달라

 

결과

SEX
   0    1 
48.2 51.8

SEX
 0  1 
48 52

위의 결과가 일반적인 백분율, 아래 결과는 정수로 반올림한 백분율을 나타낸다. 드디어 표의 두 번째 열을 채울 수 있게 되었다.

 

  빈도 백분율 누적빈도 누적백분율
여성 482 48.2    
남성 518 51.8    

 

코드 - 누적 빈도, 누적 백분율

#누적 빈도 구하기
cumsum(freq_sex)

#누적 백분율 구하기
cumsum(prop_sex_100)

cumsum(freq_sex) : freq_sex (도수분포표)의 누적 도수를 구하라.
cumsum(prop_sex_100) : prop_sex_100 (백분율)로 누적 백분율을 구하라.

 

결과

   0    1 
 482 1000 
 
     0     1 
 48.2 100.0

 

이제 표를 완성할 수 있게 되었다.

  빈도 백분율 누적빈도 누적백분율
여성 482 48.2 482 48.2
남성 518 51.8 1000 100.0

 

도수분포표 코드 전체

#작업 디렉토리 지정
setwd("C:/Users/user/Documents/Tistory_blog")

#데이터 불러오기
install.packages("readr")
library("readr")
df<-read_csv("Data.csv")

#도수분포표
freq_sex<-xtabs(~SEX, data=df) 
freq_sex                       #빈도 
prop_sex<-prop.table(freq_sex) 
prop_sex_100<-100*prop_sex
prop_sex_100                   #백분율
round(prop_sex_100,digits=0)
cumsum(freq_sex)               #누적 빈도
cumsum(prop_sex_100)           #누적 백분율

 

 

 

분할표

분할표를 작성해보자. 코드는 다음과 같다.

코드 - 빈도

#각 셀의 빈도 구하기
freq_sex_alc<-xtabs(~SEX+ALCOHOL, data=df)
freq_sex_alc

#행 별로 합친 빈도 구하기
freq_sex<-margin.table(freq_sex_alc, margin=1)
freq_sex

#열 별로 합친 빈도 구하기
freq_alc<-margin.table(freq_sex_alc, margin=2)
freq_alc

#전체 데이터 수 구하기
total_freq_sex_alc<-sum(freq_sex_alc) #혹은 sum(freq_sex)이나 sum(freq_alc)도 가능
total_freq_sex_alc

freq_sex_alc<-xtabs(~SEX+ALCOHOL, data=df) : ( '+' 표시 앞에 있는) SEX를 세로축에, ('+' 표시 뒤에 있는) ALCOHOL을 가로축에 넣고 분할표를 만들고 freq_sex_alc에 저장하라. 이때 데이터는 df를 사용하라.
freq_sex_alc : 만든 분할표를 보여달라.
freq_sex<-margin.table(freq_sex_alc, margin=1) :행 별로 합친 도수분포표 만들고 freq_sex에 저장하라. 즉, 성별의 도수분포표를 보여달라는 뜻이고 본 데이터에는 결측치가 없으므로 이는 "xtabs(~SEX, data=df)"와 같은 결과를 보여준다. (결측치가 있으면 결과가 다를 수 있다.)
freq_sex : 성별의 도수분포표를 보여달라.
freq_alc<-margin.table(freq_sex_alc, margin=2) :열 별로 합친 도수분포표 만들고 freq_alc에 저장하라. 즉, 음주 여부의 도수분포표를 보여달라는 뜻이고 본 데이터에는 결측치가 없으므로 이는 "xtabs(~ALCOHOL, data=df)"와 같은 결과를 보여준다. (결측치가 있으면 결과가 다를 수 있다.)
freq_alc : 음주 여부의 도수분포표를 보여달라.

total_freq_sex_alc<-sum(freq_sex_alc) : 빈도를 모두 더해 total_freq_sex_alc에 저장하라.
total_freq_sex_alc :총빈도수를 보여달라.

 

 

결과

   ALCOHOL
SEX   0   1
  0 236 246
  1 174 344
  
SEX
  0   1 
482 518 

ALCOHOL
  0   1 
410 590

[1] 1000

 

xtabs()함수의 진가는 분할표를 만들 때에 나온다. table()함수에는 나오지 않는 각 변수명을 보여주니 말이다. 위 데이터로 표의 빈도를 채울 수 있게 되었다.

빈도
백분율
행백분율
열백분율
비음주자 음주자 합계
여성 236 246 482
남성 174 344 518
합계 410 590 1000

 

코드 - 백분율

#각 셀의 백분율
prop_sex_alc<-prop.table(freq_sex_alc)
prop_sex_alc_100<-100*prop_sex_alc
prop_sex_alc_100

#행 별로 합친 데이터의 백분율
prop_sex<-prop.table(freq_sex)
prop_sex_100<-100*prop_sex
prop_sex_100

#열 별로 합친 데이터의 백분율
prop_alc<-prop.table(freq_alc)
prop_alc_100<-100*prop_alc
prop_alc_100

#전체 데이터의 백분율
prop_total<-prop.table(total_freq_sex_alc)
prop_total_100<-100*prop_total
prop_total_100

prop_sex_alc<-prop.table(freq_sex_alc) :2*2 분할표에서 각 셀의 분율을 구하고 prop_sex_alc에 저장하라
prop_sex_alc_100<-100*prop_sex_alc : 분율에 100을 곱하여 백분율을 구하고 prop_sex_alc_100에 저장하라
prop_sex_alc_100 : 백분율을 보여달라

 

prop_sex<-prop.table(freq_sex) :행 별로 합친 데이터의 분율을 구하고 prop_sex에 저장하라
prop_sex_100<-100*prop_sex : 분율에 100을 곱하여 백분율을 구하고 prop_sex_100에 저장하라
prop_sex_100 : 백분율을 보여달라

 

prop_alc<-prop.table(freq_sex) :열 별로 합친 데이터의 분율을 구하고 prop_alc에 저장하라
prop_alc_100<-100*prop_alc : 분율에 100을 곱하여 백분율을 구하고 prop_alc_100에 저장하라
prop_alc_100 : 백분율을 보여달라

 

prop_total<-prop.table(total_freq_sex_alc) : 전체 데이터를 합쳤을 때 분율을 구하고 prop_total에 저장하라
prop_total_100<-100*prop_total :분율에 100을 곱하여 백분율을 구하고 prop_total_100에 저장하라
prop_total_100 : 백분율을 보여달라

결과

   ALCOHOL
SEX    0    1
  0 23.6 24.6
  1 17.4 34.4
  
SEX
   0    1 
48.2 51.8 

ALCOHOL
 0  1 
41 59

[1] 100

표의 백분율을 채울 수 있게 되었다.

빈도
백분율
행백분율
열백분율
비음주자 음주자 합계
여성 236
23.6%
246
24.6%
482
48.2%
남성 174
17.4%
344
34.4%
518
51.8%
합계 410
41.0%
590
59.0%
1000
100.0%

백분율은 가로로 합하면 가로 합계의 백분율과 일치하고, 세로로 더하면 세로 합계의 백분율과 일치한다.

 

코드  - 행백분율

sum_row<-rowSums(freq_sex_alc)
prop_row<-freq_sex_alc/sum_row
prop_row_100<-100*prop_row
prop_row_100
round(prop_row_100, digits=2)

sum_row<-rowSums(freq_sex_alc) : 행 별로 더한 값을 sum_row에 저장한다. 즉 남성과 여성이 각각 몇 명이 있는지 보여준다. 이 값은 겉으로 보기엔 "freq_sex"와 같다. 하지만 속성이 약간 다르며 freq_sex로 대체할 수 없다.
prop_row<-freq_sex_alc/sum_row : 각 셀의 빈도를 행별로 더한 값으로 나눈다. 즉, 음주자건 비음주자건 남성이면 남성의 수로 나누고, 여성이면 여성의 수로 나눈다. 그리하여 분율을 구한다.
prop_row_100<-100*prop_row : 분율에 100을 곱해 백분율을 구한다. 
prop_row_100 : 백분율을 보여달라.

round(prop_row_100, digits=2) : 소수점 셋째 자리에서 반올림하여 둘째 자리까지만 표기하라.

 

결과

   ALCOHOL
SEX        0        1
  0 48.96266 51.03734
  1 33.59073 66.40927
  
   ALCOHOL
SEX     0     1
  0 48.96 51.04
  1 33.59 66.41

반올림한 결과가 훨씬 보기 편한 걸 알 수 있다. 그리고 가로로 합하면 100이 나오는 것을 볼 수 있다.

 

표의 행백분율을 채울 수 있게 되었다.

빈도
백분율
행백분율
열백분율
비음주자 음주자 합계
여성 236
23.6%
48.96%
246
24.6%
51.04%
482
48.2%
남성 174
17.4%
33.59%
344
34.4%
66.41%
518
51.8%
합계 410
41.0%
590
59.0%
1000
100.0%

 

코드 - 열백분율

R 특성상 조금 더 복잡해진다.

sum_col<-colSums(freq_sex_alc)
prop_col<-t(t(freq_sex_alc)/sum_col)
prop_col_100<-100*prop_col
prop_col_100
round(prop_col_100, digits=2)

sum_col<-colSums(freq_sex_alc) : 열 별로 더한 값을 sum_col에 저장한다. 즉 음주자와 비음주자가 각각 몇 명이 있는지 보여준다. 이 값은 겉으로 보기엔 "freq_alc"와 같다. 하지만 속성이 약간 다르며 freq_alc로 대체할 수 없다.
prop_col<-t(t(freq_sex_alc)/sum_col) : R에서 나눌 때에는 행 별로 나눈다. 즉 열 별로 나누려면 행/열을 바꾼 다음에 나누어야 한다. 어려운 말로 전치 행렬을 구해야 한다는 것이고 전치 행렬을 구하는 명령어가 t()다. 나눈 다음에 다시 한번 행/열을 바꾸지 않으면 세로에 음주 여부, 가로에 성별에 들어가 있으므로 보기에 편하려면 다시 한번 전치 행렬을 구해야 한다.
prop_col_100<-100*prop_col : 분율에 100을 곱해 백분율을 구한다. 
prop_col_100 : 백분율을 보여달라.
round(prop_col_100, digits=2) : 소수점 셋째 자리에서 반올림하여 둘째 자리까지만 표기하라.

 

결과

   ALCOHOL
SEX        0        1
  0 57.56098 41.69492
  1 42.43902 58.30508
  
   ALCOHOL
SEX     0     1
  0 57.56 41.69
  1 42.44 58.31

 

표를 완성할 수 있다.

빈도
백분율
행백분율
열백분율
비음주자 음주자 합계
여성 236
23.6%
48.96%
57.56%
246
24.6%
51.04%
41.69%

482
48.2%
남성 174
17.4%
33.59%
42.44%

344
34.4%
66.41%
58.31%

518
51.8%
합계 410
41.0%
590
59.0%
1000
100.0%

 

분할표 코드 전체

#작업 디렉토리 지정
setwd("C:/Users/user/Documents/Tistory_blog")

#데이터 불러오기
install.packages("readr")
library("readr")
df<-read_csv("Data.csv")

########빈도########

#각 셀의 빈도 구하기
freq_sex_alc<-xtabs(~SEX+ALCOHOL, data=df)
freq_sex_alc

#행 별로 합친 빈도 구하기
freq_sex<-margin.table(freq_sex_alc, margin=1)
freq_sex

#열 별로 합친 빈도 구하기
freq_alc<-margin.table(freq_sex_alc, margin=2)
freq_alc

#전체 데이터 수 구하기
total_freq_sex_alc<-sum(freq_sex_alc) #혹은 sum(freq_sex)이나 sum(freq_alc)도 가능
total_freq_sex_alc



########백분율########

#각 셀의 백분율
prop_sex_alc<-prop.table(freq_sex_alc)
prop_sex_alc_100<-100*prop_sex_alc
prop_sex_alc_100

#행 별로 합친 데이터의 백분율
prop_sex<-prop.table(freq_sex)
prop_sex_100<-100*prop_sex
prop_sex_100

#열 별로 합친 데이터의 백분율
prop_alc<-prop.table(freq_alc)
prop_alc_100<-100*prop_alc
prop_alc_100

#전체 데이터의 백분율
prop_total<-prop.table(total_freq_sex_alc)
prop_total_100<-100*prop_total
prop_total_100


########행백분율########

sum_row<-rowSums(freq_sex_alc)
prop_row<-freq_sex_alc/sum_row
prop_row_100<-100*prop_row
prop_row_100
round(prop_row_100, digits=2)


########열백분율########

sum_col<-colSums(freq_sex_alc)
prop_col<-t(t(freq_sex_alc)/sum_col)
prop_col_100<-100*prop_col
prop_col_100
round(prop_col_100, digits=2)

 

세 개 이상의 변수를 사용하는 분할표

코드

freq_sex_alc_htn<-xtabs(~SEX+ALCOHOL+HTN, data=df)
freq_sex_alc_htn

freq_sex_alc_htn<-xtabs(~SEX+ALCOHOL+HTN, data=df) : 데이터는 df를 사용하되 세로축에 SEX, 가로축에 ALCOHOL을 놓는다. HTN 값 별로 분할표를 각각 만들어 freq_sex_alc_htn에 저장한다.
freq_sex_alc_htn : 각각의 분할표를 보여달라.

 

결과

, , HTN = 0

   ALCOHOL
SEX   0   1
  0 114 122
  1  89 178

, , HTN = 1

   ALCOHOL
SEX   0   1
  0 122 124
  1  85 166

고혈압이 없는 (HTN=0) 사람들과 고혈압이 있는 (HTN=1) 사람들의 분할표를 각각 보여준다. 인구를 고혈압뿐만 아니라 RH혈액형으로도 나누고 싶다면 HTN뒤에 "+RH"를 붙여 다음과 같은 코드를 쓰면 된다.

 

#코드
freq_sex_alc_htn_rh<-xtabs(~SEX+ALCOHOL+HTN+RH, data=df)
freq_sex_alc_htn_rh

#결과
, , HTN = 0, RH = 0

   ALCOHOL
SEX   0   1
  0   0   0
  1   2   2

, , HTN = 1, RH = 0

   ALCOHOL
SEX   0   1
  0   1   0
  1   1   0

, , HTN = 0, RH = 1

   ALCOHOL
SEX   0   1
  0 114 122
  1  87 176

, , HTN = 1, RH = 1

   ALCOHOL
SEX   0   1
  0 121 124
  1  84 166

 

SAS에는 인구를 나누는 변수를 맨 앞에 쓰지만, R에서는 맨 뒤에 쓴다는 것을 유의해야 한다.

SAS에서의 분할표 작성법:

2022.08.18 - [기술 통계/SAS] - [SAS] 도수분포표 (Frequency table), 분할표 (Contingency table) 만들기 - PROC FREQ

 

[SAS] 도수분포표 (Frequency table), 분할표 (Contingency table) 만들기 - PROC FREQ

[SAS] 도수분포표 (Frequency table), 분할표 (Contingency table) 만들기 - PROC FREQ  수천 명의 정보를 포함한 데이터를 한눈에 요약하고 싶을 때가 많다. 나이, 혈압과 같은 연속형 변수는 평균으..

medistat.tistory.com

 

결론적으로 R로 분할표를 만드는 것은 매우 귀찮은 일이기 때문에 SPSS나 SAS를 쓰거나 CrossTable()함수를 쓰길 권한다.

2022.08.31 - [기술 통계/R] - [R] 도수분포표 (Frequency table), 분할표 (Contingency table) 만들기 - CrossTable()

 

[R] 도수분포표 (Frequency table), 분할표 (Contingency table) 만들기 정복 완료!

 

작성일: 2022.08.31.

최종 수정일: 2022.08.31.

이용 프로그램: R 4.1.3

RStudio v1.4.1717

RStudio 2021.09.1+372 "Ghost Orchid" Release 

운영체제: Windows 10, Mac OS 10.15.7

반응형

+ Recent posts