그룹별 요약 통계 산출

Author

Mike K Smith

Published

February 14, 2023

이 프로젝트에서는 연속형 변수에 대한 그룹별 요약 통계(Summary Statistics)를 생성하고, 이를 인구통계 요약표 형식에 맞춰 전치하는 방법을 배웁니다.

목표

  • ADSL SAS (xpt) 데이터셋 읽기
  • 유효성 분석 대상군(Efficacy Population) 서브셋 추출
  • 그룹화 변수별 요약 통계 산출
  • 표 작성을 위한 데이터 전치(Transpose)

이 문서 사용법

지침에 따라 코드 청크를 수정하고 실행하며 출력을 확인해 보세요.

데이터 소스

익명화된 CDISC 데이터셋: https://github.com/phuse-org/phuse-scripts/tree/master/data/adam/cdisc

미니 프로젝트 시작

  1. 먼저 필요한 패키지인 tidyverserio를 로드합니다.
library(tidyverse)
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)
  1. 데이터를 읽어오고 유효성 분석 대상군을 필터링합니다. 성별 레이블도 함께 재코딩합니다.
adsl <- import(file = "https://github.com/phuse-org/phuse-scripts/raw/master/data/adam/cdisc/adsl.xpt")

adsl_eff <- adsl %>%
  filter( EFFFL == "Y" ) %>%
  mutate(SEX = recode(SEX, "M" = "Male", "F" = "Female")) 
  1. 치료군 및 성별별 연령(AGE) 평균 산출.

summarize() 함수를 사용하여 요약 통계를 계산합니다. 이는 SAS의 PROC MEANS와 유사합니다.

질문: 3개의 치료군과 2개의 성별이 있을 때, TRT01AN, TRT01A, SEX로 그룹화하면 결과 데이터는 몇 행이 될까요?

adsl_eff %>%
  group_by( TRT01AN, TRT01A, SEX ) %>% 
  summarize(mean = mean(AGE), .groups = 'drop')
# A tibble: 6 × 4
  TRT01AN TRT01A               SEX     mean
    <dbl> <chr>                <chr>  <dbl>
1       0 Placebo              Female  76.1
2       0 Placebo              Male    73.4
3      54 Xanomeline Low Dose  Female  76.4
4      54 Xanomeline Low Dose  Male    75.6
5      81 Xanomeline High Dose Female  74.3
6      81 Xanomeline High Dose Male    73.6

평균값의 소수점 자릿수를 정리하고 문자형으로 변환해 보겠습니다.

adsl_eff %>%
  group_by( TRT01A, TRT01AN, SEX ) %>%
  summarize(mean = mean(AGE) %>% 
                      round(digits = 1) %>%
                      format(nsmall = 1),
            .groups = 'drop')
# A tibble: 6 × 4
  TRT01A               TRT01AN SEX    mean 
  <chr>                  <dbl> <chr>  <chr>
1 Placebo                    0 Female 76.1 
2 Placebo                    0 Male   73.4 
3 Xanomeline High Dose      81 Female 74.3 
4 Xanomeline High Dose      81 Male   73.6 
5 Xanomeline Low Dose       54 Female 76.4 
6 Xanomeline Low Dose       54 Male   75.6 
  1. 다양한 요약 통계 산출.

표준편차(sd), 중앙값(median), 최솟값(min), 최대값(max), 빈도(n)를 함께 계산합니다.

age_stat <- adsl_eff %>%
  group_by(TRT01AN, TRT01A, SEX) %>%
  summarize(mean = mean(AGE) %>% round(digits = 1) %>% format(nsmall = 1),
            sd = sd(AGE) %>% round(digits = 1) %>% format(nsmall = 1), 
            med = median(AGE) %>% format(nsmall = 0),              
            min = min(AGE) %>% format(nsmall = 0), 
            max = max(AGE) %>% format(nsmall = 0),
            n = n(),
            .groups = 'drop')

head(age_stat)
# A tibble: 6 × 9
  TRT01AN TRT01A               SEX    mean  sd    med   min   max       n
    <dbl> <chr>                <chr>  <chr> <chr> <chr> <chr> <chr> <int>
1       0 Placebo              Female 76.1  8.5   77.5  59    88       46
2       0 Placebo              Male   73.4  8.1   74    52    85       33
3      54 Xanomeline Low Dose  Female 76.4  7.6   78    56    87       47
4      54 Xanomeline Low Dose  Male   75.6  8.7   77.5  51    88       34
5      81 Xanomeline High Dose Female 74.3  7.5   76    56    88       35
6      81 Xanomeline High Dose Male   73.6  8.3   75    56    86       39
  1. 범위(Range) 형식 만들기.

minmax를 결합하여 “(min, max)” 형태의 문자열을 만듭니다.

age_stat2 <- age_stat %>%
  mutate(range_minmax = paste0("(", min, ", ", max, ")"))

head(age_stat2)
# A tibble: 6 × 10
  TRT01AN TRT01A          SEX   mean  sd    med   min   max       n range_minmax
    <dbl> <chr>           <chr> <chr> <chr> <chr> <chr> <chr> <int> <chr>       
1       0 Placebo         Fema… 76.1  8.5   77.5  59    88       46 (59, 88)    
2       0 Placebo         Male  73.4  8.1   74    52    85       33 (52, 85)    
3      54 Xanomeline Low… Fema… 76.4  7.6   78    56    87       47 (56, 87)    
4      54 Xanomeline Low… Male  75.6  8.7   77.5  51    88       34 (51, 88)    
5      81 Xanomeline Hig… Fema… 74.3  7.5   76    56    88       35 (56, 88)    
6      81 Xanomeline Hig… Male  73.6  8.3   75    56    86       39 (56, 86)    
  1. 데이터 전치 - Long Format으로 변환.

각 통계량이 열로 나열된 현재 구조를, 통계량 이름(category)과 값(values)이 행으로 나열되는 긴 형식(Long Format)으로 바꿉니다. pivot_longer()를 사용합니다.

SAS 사용자를 위한 팁: select()는 SAS의 KEEP 문과 유사합니다.

desc_stat_long <- age_stat2 %>%
  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")

head(desc_stat_long)
# A tibble: 6 × 4
  TRT01A  SEX    category     values  
  <chr>   <chr>  <chr>        <chr>   
1 Placebo Female n            46      
2 Placebo Female mean         76.1    
3 Placebo Female med          77.5    
4 Placebo Female sd           8.5     
5 Placebo Female range_minmax (59, 88)
6 Placebo Male   n            33      
  1. 데이터 전치 - Wide Format으로 변환.

표 형식에 맞춰 치료군과 성별을 열로 배치합니다. pivot_wider()를 사용합니다.

desc_stat_long %>%
  pivot_wider(names_from = c(TRT01A, SEX), values_from = values)
# A tibble: 5 × 7
  category     Placebo_Female Placebo_Male `Xanomeline Low Dose_Female`
  <chr>        <chr>          <chr>        <chr>                       
1 n            46             33           47                          
2 mean         76.1           73.4         76.4                        
3 med          77.5           74           78                          
4 sd           8.5            8.1          7.6                         
5 range_minmax (59, 88)       (52, 85)     (56, 87)                    
# ℹ 3 more variables: `Xanomeline Low Dose_Male` <chr>,
#   `Xanomeline High Dose_Female` <chr>, `Xanomeline High Dose_Male` <chr>
  1. 최종 레이블 정리.

category의 변수명을 실제 표에 표시될 이름으로 변경합니다. case_when()을 활용합니다.

agestat_cat <- desc_stat_long %>%
  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)")) 

agestat_cat
# A tibble: 5 × 7
  category         Placebo_Female Placebo_Male `Xanomeline Low Dose_Female`
  <chr>            <chr>          <chr>        <chr>                       
1 N                46             33           47                          
2 Mean             76.1           73.4         76.4                        
3 Median           77.5           74           78                          
4 Std Dev          8.5            8.1          7.6                         
5 Range (min, max) (59, 88)       (52, 85)     (56, 87)                    
# ℹ 3 more variables: `Xanomeline Low Dose_Male` <chr>,
#   `Xanomeline High Dose_Female` <chr>, `Xanomeline High Dose_Male` <chr>

챌린지 1: 체중(Weight) 및 신장(Height) 분석

위 과정을 반복하여 다른 연속형 변수에 대한 요약표를 만들어 보세요.

보너스 팁: 함수(Function) 작성

동일한 작업을 반복해야 한다면 함수를 작성하는 것이 좋습니다.

varSummary <- function(.data, variable) {
  .data %>%
    group_by(TRT01AN, TRT01A, SEX) %>%
    summarize(
      mean = mean({{variable}}) %>% round(digits = 1) %>% format(nsmall = 1),
      sd = sd({{variable}}) %>% round(digits = 1) %>% format(nsmall = 1),
      med = median({{variable}}) %>% format(nsmall = 0),
      min = min({{variable}}) %>% format(nsmall = 0),
      max = max({{variable}}) %>% format(nsmall = 0),
      n = n(),
      .groups = 'drop'
    )
}

adsl_eff %>%
  varSummary(AGE)
# A tibble: 6 × 9
  TRT01AN TRT01A               SEX    mean  sd    med   min   max       n
    <dbl> <chr>                <chr>  <chr> <chr> <chr> <chr> <chr> <int>
1       0 Placebo              Female 76.1  8.5   77.5  59    88       46
2       0 Placebo              Male   73.4  8.1   74    52    85       33
3      54 Xanomeline Low Dose  Female 76.4  7.6   78    56    87       47
4      54 Xanomeline Low Dose  Male   75.6  8.7   77.5  51    88       34
5      81 Xanomeline High Dose Female 74.3  7.5   76    56    88       35
6      81 Xanomeline High Dose Male   73.6  8.3   75    56    86       39

참고: 함수 내부에서 데이터셋의 변수를 참조할 때는 { } (Embrace 연산자)를 사용해야 R이 해당 변수를 인식할 수 있습니다.