이 프로젝트에서는 이전 프로젝트(2, 3)에서 작성한 코드를 결합하여 인구통계 요약표(Demography Summary Table)의 최종 데이터를 구성하는 방법을 배웁니다.
목표
빈도와 백분율 데이터 산출
기술 통계량 데이터 산출
행 기반 결합(bind_rows)을 통한 최종 데이터셋 생성
미니 프로젝트 시작
ADSL 데이터 로드 및 전처리
Warning: package 'ggplot2' was built under R version 4.4.3
Warning: package 'tibble' was built under R version 4.4.3
Warning: package 'purrr' was built under R version 4.4.2
Warning: package 'lubridate' was built under R version 4.4.2
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.2 ✔ tibble 3.3.0
✔ lubridate 1.9.4 ✔ tidyr 1.3.1
✔ purrr 1.0.4
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library (rio)
adsl <- import (file = "./data/adsl.xpt" )
adsl_eff <- adsl %>%
filter (EFFFL == "Y" ) %>%
mutate (SEX = recode (SEX, "M" = "Male" , "F" = "Female" ))
치료군 및 성별별 빈도(n)와 총계(N) 산출
Big_N_cnt <- adsl_eff %>%
group_by (TRT01AN, TRT01A) %>%
count (name = "N" )
small_n_cnt <- adsl_eff %>%
group_by (TRT01AN, TRT01A, SEX) %>%
count (name = "n" )
여담: 빈도가 0인 범주 처리하기
데이터에 특정 범주의 관측치가 없는 경우, 해당 행이 누락될 수 있습니다. 이를 방지하기 위해 complete() 함수를 사용하여 모든 가능한 조합을 생성하고 빈도를 0으로 채울 수 있습니다.
myData <- tribble (
~ TRT01AN, ~ TRT01A, ~ SEX,
1 , "Placebo" , "M" ,
1 , "Placebo" , "F" ,
2 , "Active" , "F" ,
2 , "Active" , "F" ,
3 , "Comparator" , "M" ,
3 , "Comparator" , "M"
)
# complete를 사용하여 누락된 조합(Active-Male, Comparator-Female)을 0으로 채움
myData %>%
group_by (TRT01AN, TRT01A, SEX) %>%
count (name = "n" ) %>%
ungroup () %>%
complete (nesting (TRT01AN, TRT01A), SEX, fill = list (n = 0 ))
# A tibble: 6 × 4
TRT01AN TRT01A SEX n
<dbl> <chr> <chr> <int>
1 1 Placebo F 1
2 1 Placebo M 1
3 2 Active F 2
4 2 Active M 0
5 3 Comparator F 0
6 3 Comparator M 2
연령 그룹(Age Group) 빈도 산출 및 병합
Agegrp_N_cnt <- adsl_eff %>%
group_by (TRT01AN, TRT01A, AGEGR1) %>%
count (name = "age_total" )
age_n_cnt <- adsl_eff %>%
group_by (TRT01AN, TRT01A, SEX, AGEGR1) %>%
count (name = "age_n" )
age_mrg_cnt <- age_n_cnt %>%
left_join (Agegrp_N_cnt, by = c ("TRT01AN" , "TRT01A" , "AGEGR1" )) %>%
left_join (Big_N_cnt, by = c ("TRT01AN" , "TRT01A" )) %>%
left_join (small_n_cnt, by = c ("TRT01A" , "TRT01AN" , "SEX" )) %>%
ungroup ()
백분율 계산 및 포맷 지정
age_n_pct <- age_mrg_cnt %>%
mutate (perc_age = round ((age_n/ n)* 100 , 1 )) %>%
mutate (perc_achar = format (perc_age, nsmall = 1 )) %>%
mutate (npct = paste (age_n, paste0 ("(" , perc_achar, ")" ))) %>%
select (AGEGR1, TRT01A, SEX, npct)
데이터 전치 및 레이블 변경
age_cat <- age_n_pct %>%
pivot_wider (names_from = c (TRT01A, SEX),
values_from = npct,
values_fill = "0" ,
names_sep = "_" ) %>%
rename (category = AGEGR1) %>%
arrange (category)
여담: 팩터(Factor)를 이용한 정렬 순서 제어
R은 문자형 변수를 기본적으로 영숫자 순으로 정렬합니다. 연령 그룹처럼 논리적 순서가 있는 경우 factor()를 사용하여 순서를 명시적으로 정의하는 것이 좋습니다.
myData <- tibble:: tribble (
~ ID, ~ age,
1 , "18-44" ,
2 , ">=65" ,
3 , "45-64" )
# 원하는 순서로 레벨 지정
myData <- myData %>%
mutate (age = factor (age, levels = c ("18-44" , "45-64" , ">=65" )))
myData %>% arrange (age)
# A tibble: 3 × 2
ID age
<dbl> <fct>
1 1 18-44
2 3 45-64
3 2 >=65
연령 기술 통계량 산출 (프로젝트 3 복습)
age_stat <- adsl_eff %>%
group_by (TRT01AN, TRT01A, SEX) %>%
summarize (mean = mean (AGE) %>% round (1 ) %>% format (nsmall = 1 ),
sd = sd (AGE) %>% round (1 ) %>% format (nsmall = 1 ),
med = median (AGE) %>% round (1 ) %>% format (nsmall = 1 ),
min = min (AGE) %>% format (nsmall = 1 ),
max = max (AGE) %>% format (nsmall = 1 ),
n = n () %>% as.character (),
.groups = 'drop' ) %>%
mutate (range_minmax = paste0 ("(" , min, ", " , max, ")" ))
기술 통계량 데이터 전치
agestat_cat <- age_stat %>%
select (TRT01A, SEX, n, mean, med, sd, range_minmax) %>%
mutate (across (where (is.numeric), as.character)) %>%
pivot_longer (- c (TRT01A, SEX), names_to = "category" , values_to = "values" ) %>%
pivot_wider (names_from = c (TRT01A, SEX), values_from = values) %>%
mutate (category = case_when (category == "n" ~ "N" ,
category == "med" ~ "Median" ,
category == "mean" ~ "Mean" ,
category == "sd" ~ "Std Dev" ,
category == "range_minmax" ~ "Range (min, max)" ))
최종 데이터 결합
bind_rows() 함수를 사용하여 위에서 만든 범주형 빈도 데이터(age_cat)와 연속형 요약 통계 데이터(agestat_cat)를 위아래로 합칩니다. 이는 SAS의 SET 문과 유사한 역할을 합니다.
dm_allcomb <- bind_rows (age_cat, agestat_cat)
dm_allcomb
# A tibble: 8 × 7
category Placebo_Female Placebo_Male `Xanomeline Low Dose_Female`
<chr> <chr> <chr> <chr>
1 65-80 20 (43.5) 20 (60.6) 26 (55.3)
2 <65 8 (17.4) 5 (15.2) 4 ( 8.5)
3 >80 18 (39.1) 8 (24.2) 17 (36.2)
4 N 46 33 47
5 Mean 76.1 73.4 76.4
6 Median 77.5 74.0 78.0
7 Std Dev 8.5 8.1 7.6
8 Range (min, max) (59.0, 88.0) (52.0, 85.0) (56.0, 87.0)
# ℹ 3 more variables: `Xanomeline Low Dose_Male` <chr>,
# `Xanomeline High Dose_Female` <chr>, `Xanomeline High Dose_Male` <chr>
챌린지
연령 범주의 표시 순서를 적절하게 조정해 보세요. (<65, 65-80, >80)
N (전체 대상자 수) 행을 연령 범주 행보다 위로 이동시켜 보세요.
민족(Ethnicity)과 인종(Race) 변수에 대해서도 동일한 과정을 수행하여 요약표를 확장해 보세요.