[SPSS] 기술 통계 (평균, 표준편차, 표준오차, 최댓값, 최솟값, 중위수, 분위수 등)
1,000명으로 어떤 연구를 했다고 하자. 그들의 키, 몸무게 등 지표들은 서로 다를 것이다. 논문의 저자가 이 모든 것을 독자들에게 보여주고자 한다면 행이 1,000인 표를 제시해야 할 것이다. 그렇게 큰 표를 실어줄 저널이 없기도 하거니와, 독자들이 보기에도 한눈에 들어오지 않는다. 그 대신 키의 '평균', 몸무게의 '평균'을 제시하면 한눈에 들어오니 보기가 좋다. 연속 변수는 평균, 표준편차 등으로 요약을 하여 보여주고, 범주형 자료 (흡연 여부, 음주 여부 등)는 도수분포표 혹은 분할표로 제시하게 된다. 분할표를 작성하는 방법은 다음 링크에서 확인할 수 있다.
정규성을 따르지 않는다면 평균과 표준편차를 안다고 해도 전체 분포를 알아낼 수는 없다. 따라서 분포에 대한 직접적인 정보를 주는데, 예를 들어 '하위 25%에 위치하는 사람의 ALT값은 얼마인가?' 등을 제시하는 것이다. 그런 지표로는 중위수, 최댓값, 최솟값, 분 위수, 사분위 범위 등이 있다.
이번 포스팅에서는 이 모든 지표들 (평균, 표준편차, 표준오차, 중위수, 최댓값, 최솟값, 분위수, 사분위 범위 등)을 구하는 법에 대해 소개할 것이다.
1,000명으로 어떤 연구를 했다고 하자. 그들의 키, 몸무게 등 지표들은 서로 다를 것이다. 논문의 저자가 이 모든 것을 독자들에게 보여주고자 한다면 행이 1,000인 표를 제시해야 할 것이다. 그렇게 큰 표를 실어줄 저널이 없기도 하거니와, 독자들이 보기에도 한눈에 들어오지 않는다. 그 대신 키의 '평균', 몸무게의 '평균'을 제시하면 한눈에 들어오니 보기가 좋다. 연속 변수는 평균, 표준편차 등으로 요약을 하여 보여주고, 범주형 자료 (흡연 여부, 음주 여부 등)는 도수분포표 혹은 분할표로 제시하게 된다. 분할표를 작성하는 방법은 다음 링크에서 확인할 수 있다.
정규성을 따르지 않는다면 평균과 표준편차를 안다고 해도 전체 분포를 알아낼 수는 없다. 따라서 분포에 대한 직접적인 정보를 주는데, 예를 들어 '하위 25%에 위치하는 사람의 ALT값은 얼마인가?' 등을 제시하는 것이다. 그런 지표로는 중위수, 최댓값, 최솟값, 분 위수, 사분위 범위 등이 있다.
이번 포스팅에서는 이 모든 지표들 (평균, 표준편차, 표준오차, 중위수, 최댓값, 최솟값, 분위수, 사분위 범위 등)을 구하는 법에 대해 소개할 것이다.
univariate<-function(x){:입력되는 값이 x인 함수를 만들건데 함수의 내용은 { }안에 들어있으니 살펴봐라. 그리고 함수의 이름은 univariate이다. n<-length(x)-sum(is.na(x)): 분석 대상의 수[n]는 전체 개체의 수[length(x)]에서 결측치의 개수 [sum(is.na(x))]를 뺀 값이다. missing<-sum(is.na(x)): 결측된 것을 TRUE로, 결측되지 않은 것을 FALSE로 반환한다 [is.na(x)]. TRUE인 것을 모두 합친다 [sum(is.na(x))]. 그 값이 결측치의 개수이다. mean<-mean(x): 평균은 mean() 함수를 이용한다. sd<-sd(x): 표준편차는 sd() 함수를 이용한다. var<-var(x): 분산은 var() 함수를 이용한다. se<-sqrt(var/n): 표준오차(se)는 분산(var)을 분석 대상의 수(n)로 나눈 뒤 루트를 씌운 값이다.
min<-min(x): 최솟값은 min() 함수를 이용한다. max<-max(x): 최댓값은 max() 함수를 이용한다. median<-median(x): 중앙값은 median() 함수를 이용한다. mode <- function(x) {: 최빈값은 구하는 함수가 R에 내장되어있지 않으므로 함수를 따로 만들어야 한다. 데이터(x)를 입력받아 최빈값을 구하는 함수를 만들 것이고 그 함수의 이름은 mode이다. nodup <- unique(x): 중복되는 값을 모두 삭제하고 한 개씩만 대표적으로 남긴다. 대표들로 이루어진 벡터를 nodup이라고 하자. if (length(which(tabulate(match(x, nodup))==max(tabulate(match(x, nodup)))))>=2 ) {NA} else{nodup[which.max(tabulate(match(x, nodup)))]}
여기까지의 내용이 복잡하니 하나씩 코드를 살펴보기로 한다.
(1) 큰 구성은 if (A) {B} else {C} 이다. A라는 조건이 만족하면 B를 반환하고, A를 만족하지 않으면 C를 반환한다는 것이다.
(2) match(x, nodup): x가 nodup에서 몇 번째 데이터와 일치하는지를 반환하는 함수다.
Example) 예를 들어 x가 (1,5,2,3,3,4,2)라고 하자.
nodup은 중복되는 값을 삭제했을 터이니 (1,5,2,3,4)일 것이다.
그렇다면 match(x,nodup)은 (1,2,3,4,4,5,3)이 된다.
(3) tabulate(match(x,nodup)): tabulate는 각 정수가 나타난 횟수를 세준다. (1,2,3,4,4,5,3)에서 1은 1개, 2는 1개, 3은 2개, 4는 2개, 5는 1개다. 따라서 tabulate(match(x,nodup))은 (1,1,2,2,1)이 된다.
최빈값은 가장 흔한 단 한 개의 값이어야 하는데, 여기에서 3과 4는 2개, 나머지는 1개이므로 이 데이터의 최빈값은 없다. 즉, tabulate(match(x,nodup))인 (1,1,2,2,1)의 최댓값(2)이 tabulate(match(x,nodup))인 (1,1,2,2,1)에 2개 이상 존재한다면 최빈값이 없다고 반환해야 한다.
(5) which(tabulate(match(x, nodup))==max(tabulate(match(x, nodup))))는 TRUE인 위치를 반환하므로 (3,4)를 보여준다.
(6) length(which(tabulate(match(x, nodup))==max(tabulate(match(x, nodup)))))는 (3,4)에 있는 숫자의 개수, 즉 2를 반환한다.
이렇게 함으로써 최빈값'같은' 값이 2개 이상 존재할 때 NA를 반환하도록 하였다.
그렇지 않을 때 (즉, 단 한 개의 최빈값이 존재할 때)는 다음과 같은 로직에 의해 구해진다고 생각하면 된다.
Example) x가 (1,5,2,2,3)이라고 하자.
nodup은 (1,5,2,3)이 된다.
match(x,nodup)은 (1,2,3,3,4)이 된다.
tabulate(match(x,nodup))는 (1,1,2,1)이 된다. 여기에서 횟수가 가장 많아 '2'라고 표현된 값의 근원을 찾아가야 한다.
which.max(tabulate(match(x, nodup)))는 (1,1,2,1)에서 최댓값이 존재하는 위치를 반환하므로 3이 된다.
(1,1,2,1)의 2는 match(x,nodup)인 (1,2,3,3,4)에서 온 것이다. 1이 1개, 2가 1개, 3이 2개, 4가 1개. 즉, tabulate는 개수를 세어주므로 중복된 값을 빼는 효과가 있다. 즉 2가 3번째에 위치한 이유는 nodup (1,5,2,3)의 2가 nodup에서 3번째에 위치해있기 때문이다. 따라서 nodup의 3번째 값을 반환해야 최빈값을 구할 수 있다. 그러므로 nodup[which.max(tabulate(match(x, nodup)))]이라는 코드를 쓰게 된다.
} mode<-mode(x)
lclm<-mean-qt(0.975, n-1)*se
uclm<-mean+qt(0.975, n-1)*se
P25<-data.frame(quantile(x)[2])[1,1]
P50<-data.frame(quantile(x)[3])[1,1]
P75<-data.frame(quantile(x)[4])[1,1]
qrange<-data.frame(quantile(x)[4])[1,1]-data.frame(quantile(x)[2])[1,1]
name<-c("N","N of missing","Mean","Standard Deviation","Variation","Standard Error","Minimum","Maximum","Median","Mode","95% Lower limit of mean (two-sided)","95% Upper limit of mean(two-sided)","P25","P50","P75","Quantile Range")
value<-c(n,missing, mean, sd, var, se,min,max, median, mode, lclm, uclm, P25, P50, P75, qrange)
result<-cbind(name, value)
print(result)}
lclm<-mean-qt(0.975, n-1)*se: t 분포에서 자유도(n-1)에 걸맞게 2.5%에 해당하는 값을 찾고, 표준오차를 곱하여 평균에서 빼면 95% 신뢰구간의 하한이 구해진다. uclm<-mean+qt(0.975, n-1)*se: t 분포에서 자유도(n-1)에 걸맞게 2.5%에 해당하는 값을 찾고, 표준오차를 곱하여 평균에서 더하면 95% 신뢰구간의 상한이 구해진다. P25<-data.frame(quantile(x)[2])[1,1]: quantile() 함수의 2번째 값은 1사분위수를 의미한다. 데이터프레임으로 바꾸고 값만을 취한다. P50<-data.frame(quantile(x)[3])[1,1]: quantile() 함수의 2번째 값은 2사분위수를 의미한다. 데이터프레임으로 바꾸고 값만을 취한다. P75<-data.frame(quantile(x)[4])[1,1]: quantile() 함수의 2번째 값은 3사분위수를 의미한다. 데이터프레임으로 바꾸고 값만을 취한다. qrange<-data.frame(quantile(x)[4])[1,1]-data.frame(quantile(x)[2])[1,1]: 3사분위수에서 1사분위수를 빼어 값을 구한다. name<-c("N", "N of missing","Mean", "Standard Deviation", "Variation", "Standard Error", "Minimum", "Maximum", "Median", "Mode", "95% Lower limit of mean (two-sided)", "95% Upper limit of mean(two-sided)", "P25", "P50", "P75", "Quantile Range"): 각 값의 이름을 지정하여 name이라는 벡터에 저장한다. value<-c(n, missing, mean, sd, var, se, min, max, median, mode, lclm, uclm, P25, P50, P75, qrange): 위에서 구한 값들로 벡터를 만들고 value라는 이름을 붙인다. result<-cbind(name, value): name과 value를 열 별로 합치고 result에 저장한다. print(result): result를 출력한다. }
만약 25%, 50%, 75%가 아닌 임의의 n% 백분위수를 구하고 싶다면 Pn을 사용하면 된다.
64%백분위수를 보고싶다면 P64를 사용하면 된다.
사분위수는 SAS, SPSS, R의 결과가 서로 다를 수 있다. 왜냐하면 각 프로그램에서 사분위수를 구하는 방법이 다를 수 있기 때문이다. 각 프로그램에는 사분위수를 구하는 여러가지 방법이 내장되어 있으며, 골라서 사용할 수도 있다.
[SAS] 기술 통계 (평균, 표준편차, 표준오차, 최댓값, 최솟값, 중위수, 분위수 등) - PROC UNIVARIATE, PROC MEANS
1,000명으로 어떤 연구를 했다고 하자. 그들의 키, 몸무게 등 지표들은 서로 다를 것이다. 논문의 저자가 이 모든 것을 독자들에게 보여주고자 한다면 행이 1,000인 표를 제시해야 할 것이다. 그렇게 큰 표를 실어줄 저널이 없기도 하거니와, 독자들이 보기에도 한눈에 들어오지 않는다. 그 대신 키의 '평균', 몸무게의 '평균'을 제시하면 한눈에 들어오니 보기가 좋다. 연속 변수는 평균, 표준편차 등으로 요약을 하여 보여주고, 범주형 자료 (흡연 여부, 음주 여부 등)는 도수분포표 혹은 분할표로 제시하게 된다. 분할표를 작성하는 방법은 다음 링크에서 확인할 수 있다. 2022.08.18 - [기술 통계/SAS] - [SAS] 도수분포표 (Frequency table), 분할표 (Contingency table) 만들기 - PROC FREQ
정규성을 따르지 않는다면 평균과 표준편차를 안다고 해도 전체 분포를 알아낼 수는 없다. 따라서 분포에 대한 직접적인 정보를 주는데, 예를 들어 '하위 25%에 위치하는 사람의 ALT값은 얼마인가?' 등을 제시하는 것이다. 그런 지표로는 중위수, 최댓값, 최솟값, 분 위수, 사분위 범위 등이 있다.
이번 포스팅에서는 이 모든 지표들 (평균, 표준편차, 표준오차, 중위수, 최댓값, 최솟값, 분위수, 사분위 범위 등)을 구하는 법에 대해 소개할 것이다.
만약, 음주 여부에 따라 기술 통계량을 보고 싶다면 다음과 같이 "CLASS"구문을 추가하면 된다.
PROC UNIVARIATE DATA=hong.df ;
CLASS ALCOHOL;
VAR ALT ;
PROC UNIVARIATE DATA=hong.df : 기술 통계량을 산출하는 코드를 시작할 것이고, 데이터는 hong 라이브러리에 있는 df를 쓰겠다.
CLASS ALCOHOL : 음주 여부에 따라서 각각 결과를 산출하라 VAR ALT : 변수 "ALT"에 대한 기술 통계량을 보여달라.
결과
경력자용 (PROC MEANS)
PROC UNIVARIATE은 정말 훌륭한 코드이지만 단점은 출력되는 결괏값이 너무 많다는 것이다. 그중에 필요한 것만 골라서 보고 싶다면 PROC MEANS가 더욱 적절하다. 물론 SAS에 익숙하지 않다면 코드가 복잡하게 느껴질 수도 있다.
기본 코드
PROC MEANS DATA=hong.df ;
VAR ALT ;
RUN;
PROC MEANS DATA=hong.df : 기술 통계량을 산출하는 코드를 시작할 것이고, 데이터는 hong 라이브러리에 있는 df를 쓰겠다. VAR ALT : 변수 "ALT"에 대한 기술 통계량을 보여달라.
결과
보고 싶은 통계량을 지정해놓지 않으면 표본의 수, 평균, 표준편차, 최솟값, 최댓값을 보여준다.
코드
보고 싶은 통계량이 있다면 데이터 지정 후 모두 다 적어주면 된다.
예를 들어 표본의 수는 N, 평균은 MEAN, 표준편차는 STD인데 이 세 가지를 넣은 코드는 다음과 같다.
PROC MEANS DATA=hong.df N MEAN STD;
VAR ALT ;
RUN;
PROC MEANS DATA=hong.df N MEAN STD : 기술 통계량을 산출하는 코드를 시작할 것이고, 데이터는 hong 라이브러리에 있는 df를 쓰겠다. 통계량은 표본의 수, 평균, 표준편차만 보여달라. VAR ALT : 변수 "ALT"에 대한 기술 통계량을 보여달라.
결과
비교적 자주 쓰는 통계량의 코드는 다음과 같다.
통계량
코드
통계량
코드
표본 수
N
평균의 신뢰구간
CLM
결측 수
NMISS
25백분위수 (1사분위수)
P25 (Q1)
평균
MEAN
75백분위수 (3사분위수)
P75 (Q3)
표준편차
STD
사분위 범위
QRANGE
표준오차
STDERR
1백분위수
P1
최솟값
MIN
5백분위수
P5
최댓값
MAX
n0백분위수
Pn0 (i.e. P10, P20, P30, ...)
중위수
MEDIAN
95백분위수
P95
최빈값
MODE
99백분위수
P99
*CLM은 양측(two-sided) 신뢰구간을 구해준다. 만약 단측 (one-sided)신뢰구간을 구하고자 한다면 신뢰구간의 상한은 UCLM을, 하한은 LCLM을 사용하면 된다.
코드
만약, 음주 여부에 따라 기술 통계량을 보고 싶다면 다음과 같이 "CLASS"구문을 추가하면 된다.
PROC MEANS DATA=hong.df N MEAN STD;
CLASS ALCOHOL;
VAR ALT ;
RUN;
PROC MEANS DATA=hong.df N MEAN STD : 기술 통계량을 산출하는 코드를 시작할 것이고, 데이터는 hong 라이브러리에 있는 df를 쓰겠다. 통계량은 표본의 수, 평균, 표준편차만 보여달라.
CLASS ALCOHOL : 음주 여부에 따라서 각각 결과를 산출하라 VAR ALT : 변수 "ALT"에 대한 기술 통계량을 보여달라.
결과
코드
만약, 음주 여부에 따라 ALT와 수축기 혈압을 동시에 보고 싶다면 변수 자리에 수축기 혈압 변수를 같이 쓰면 된다.
PROC MEANS DATA=hong.df N MEAN STD;
CLASS ALCOHOL;
VAR ALT SBP;
RUN;
PROC MEANS DATA=hong.df N MEAN STD : 기술 통계량을 산출하는 코드를 시작할 것이고, 데이터는 hong 라이브러리에 있는 df를 쓰겠다. 통계량은 표본의 수, 평균, 표준편차만 보여달라.
CLASS ALCOHOL : 음주 여부에 따라서 각각 결과를 산출하라 VAR ALT SBP : 변수 "ALT"와 "SBP"에 대한 기술 통계량을 보여달라.
결과
코드 정리
*라이브러리 지정하기;LIBNAME hong "C:/Users/User/Documents/Tistory_blog";
*파일 불러오기;PROC IMPORT
DATAFILE="C:\Users\user\Documents\Tistory_blog\Data.xlsx"
DBMS=EXCEL
OUT=hong.df
REPLACE;
RUN;*초보자용;PROC UNIVARIATE DATA=hong.df ;
VAR ALT ;
RUN;*(초보자용) 음주 여부에 따른 기술 통계량;PROC UNIVARIATE DATA=hong.df ;
CLASS ALCOHOL;
VAR ALT ;
*경력자용;PROC MEANS DATA=hong.df ;
VAR ALT ;
RUN;*(경력자용) 음주 여부에 따른 기술 통계량;PROC MEANS DATA=hong.df N MEAN STD;
CLASS ALCOHOL;
VAR ALT ;
RUN;
[SPSS] 도수분포표 (Frequency table), 분할표 (Contingency table) 만들기
수천 명의 정보를 포함한 데이터를 한눈에 요약하고 싶을 때가 많다. 나이, 혈압과 같은 연속형 변수는 평균으로 요약하곤 하는데, 성별이나 음주 여부는 평균을 구할 수 없으니 빈도를 제시하곤 한다. 이를 표로 제시하면 도수분포표 (Frequency table)가 된다. 이를 넘어서 남성 중 음주자가 몇 명인지, 여성중 비음주자가 몇 명인지 알고 싶을 때가 있는데, 이때 사용하는 것이 분할표 (Contingency table)이다. 즉 본 글의 목적은 다음 두 개의 표 내용을 채우는 것이다.
subsample_wohtn<-df[df$HTN==0,] : 데이터 df의 HTN변수가 0인 사람만 뽑아 "subsample_wohtn"에 저장한다. subsample_whtn<-df[df$HTN==1,] : 데이터 df의 HTN변수가 1인 사람만 뽑아 "subsample_whtn"에 저장한다.
Cell Contents
|-------------------------|| N || N / Row Total || N / Col Total || N / Table Total ||-------------------------|
Total Observations in Table:503| subsample_wohtn$ALCOHOL
subsample_wohtn$SEX |0|1| Row Total |--------------------|-----------|-----------|-----------|0|114|122|236||0.483|0.517|0.469||0.562|0.407|||0.227|0.243||--------------------|-----------|-----------|-----------|1|89|178|267||0.333|0.667|0.531||0.438|0.593|||0.177|0.354||--------------------|-----------|-----------|-----------|
Column Total |203|300|503||0.404|0.596||--------------------|-----------|-----------|-----------|
Cell Contents
|-------------------------|| N || N / Row Total || N / Col Total || N / Table Total ||-------------------------|
Total Observations in Table:497| subsample_whtn$ALCOHOL
subsample_whtn$SEX |0|1| Row Total |-------------------|-----------|-----------|-----------|0|122|124|246||0.496|0.504|0.495||0.589|0.428|||0.245|0.249||-------------------|-----------|-----------|-----------|1|85|166|251||0.339|0.661|0.505||0.411|0.572|||0.171|0.334||-------------------|-----------|-----------|-----------|
Column Total |207|290|497||0.416|0.584||-------------------|-----------|-----------|-----------|
[R] 도수분포표 (Frequency table), 분할표 (Contingency table) 만들기 정복 완료!
수천 명의 정보를 포함한 데이터를 한눈에 요약하고 싶을 때가 많다. 나이, 혈압과 같은 연속형 변수는 평균으로 요약하곤 하는데, 성별이나 음주 여부는 평균을 구할 수 없으니 빈도를 제시하곤 한다. 이를 표로 제시하면 도수분포표 (Frequency table)가 된다. 이를 넘어서 남성 중 음주자가 몇 명인지, 여성중 비음주자가 몇 명인지 알고 싶을 때가 있는데, 이때 사용하는 것이 분할표 (Contingency table)이다. 즉 본 글의 목적은 다음 두 개의 표 내용을 채우는 것이다.
하지만 앞으로 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
01482518
함수 xtabs()는 정말 고맙게도 변수의 이름을 표기해준다.
표의 다음 파란 글씨를 이제 채울 수 있게 되었다.
빈도
백분율
누적빈도
누적백분율
여성
482
남성
518
코드 - 분율
분율을 구하는 법은 다음과 같다.
prop.table(xtabs(~SEX, data=df))
prop.table(xtabs(~SEX, data=df)) :위에서 구한 도수분포표 [코드: xtabs(~SEX, data=df)]의 분율(proportion)을 구하라.
결과
SEX
010.4820.518
SEX=0의 분율은 0.482, SEX=1의 분율은 0.518이다.
하지만, %로 나타난 백분율이 아니므로 이대로는 표에 넣을 수 없다. 그리고 이런 식의 코드는 복잡함을 증대시키기 때문에 보통 다음과 같은 코드를 사용한다. 순서대로 설명하면 다음과 같다.
#각 셀의 빈도 구하기
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 0102362461174344
SEX
01482518
ALCOHOL
01410590[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
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 01048.9626651.03734133.5907366.40927
ALCOHOL
SEX 01048.9651.04133.5966.41
반올림한 결과가 훨씬 보기 편한 걸 알 수 있다. 그리고 가로로 합하면 100이 나오는 것을 볼 수 있다.
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 01057.5609841.69492142.4390258.30508
ALCOHOL
SEX 01057.5641.69142.4458.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)
수천 명의 정보를 포함한 데이터를 한눈에 요약하고 싶을 때가 많다. 나이, 혈압과 같은 연속형 변수는 평균으로 요약하곤 하는데, 성별이나 음주 여부는 평균을 구할 수 없으니 빈도를 제시하곤 한다. 이를 표로 제시하면 도수분포표 (Frequency table)가 된다. 이를 넘어서 남성 중 음주자가 몇 명인지, 여성중 비음주자가 몇 명인지 알고 싶을 때가 있는데, 이때 사용하는 것이 분할표 (Contingency table)이다. 즉 본 글의 목적은 다음 두 개의 표 내용을 채우는 것이다.
세 개 이상의 변수를 사용하여 분할표를 작성하고 싶을 때가 있다. 다음 두 경우를 생각해보도록 하겠다.
1) 두 가지의 분할표를 작성하는 경우
:성별-음주의 분할표와 성별-고혈압의 분할표를 각각 그리고자 할 때
*방법 1: TABLE 구문 안에 원하는 변수의 조합을 모두 쓴다;PROC FREQ DATA=hong.df;
TABLE SEX*ALCOHOL SEX*HTN;RUN;*방법 2: 공통변수로 묶은 뒤 괄호 안에 나머지 변수를 띄어쓰기로 구분하여 작성한다;PROC FREQ DATA=hong.df;
TABLE SEX*(ALCOHOL HTN);RUN;
문제 상황처럼 고혈압 여부(HTN)에 따른 성별(SEX)과 음주(ALCOHOL)의 분할표를 그리고자 한다면, 고혈압 여부(HTN)를 TABLE구문 맨 앞에 추가로 붙여준다. 시행하면 다음과 같은 결과를 얻는다.
결과
고혈압이 없는 (HTN=0) 사람의 성별-음주 분할표가 위에 나오고, 고혈압이 있는 (HTN=1) 사람의 성별-음주 분할표가 따라 나오게 된다.
- SAS를 조금 아는 사람이라면 "WHERE 구문을 쓰면 되는데 왜 이렇게 복잡하게 하냐?"라고 이야기할 수도 있지만, 국민건강 영양조사(NHANES, KNHANES) 자료를 쓸 때엔 WHERE 구문이나 BY 구문의 사용이 엄격하게 금지되므로 이 방법이 필수적이다. 물론 이땐 PROC FREQ이 아니라 PROC SURVEYFREQ을 사용하게 된다.
- SAS에는 인구를 나누는 변수를 맨 앞에 쓰지만, R에서는 맨 뒤에 쓴다는 것을 유의해야 한다.