# 단일 열(SUBJID)의 비결측치 개수 계산
adsl_saf %>%
summarise(nonmiss = sum(!is.na(SUBJID))) nonmiss
1 254
데이터의 행, 열 또는 서브셋에 대해 함수를 반복적으로 적용하는 방법을 배웁니다. R의 강점인 벡터화(Vectorization)와 반복문을 효율적으로 사용하는 방법을 살펴봅니다.
apply 계열 함수 활용{tidyverse}의 across() 및 rowwise() 활용{purrr} 패키지를 이용한 리스트 기반 반복 처리SAS에서는 DATA 단계를 통해 행 단위로 처리하는 방식이 익숙하지만, R은 벡터(Vector) 단위 처리에 최적화되어 있습니다. 예를 들어, 모든 열의 비결측치 개수를 셀 때 R은 벡터 전체를 한 번에 평가합니다.
# 단일 열(SUBJID)의 비결측치 개수 계산
adsl_saf %>%
summarise(nonmiss = sum(!is.na(SUBJID))) nonmiss
1 254
R은 is.na() 함수를 벡터의 모든 요소에 동시에 적용하여 TRUE/FALSE 벡터를 반환하고, sum()은 이를 1/0으로 취급하여 합계를 구합니다. 명시적인 루프 없이도 매우 빠르게 처리됩니다.
모든 열에 대해 같은 작업을 반복해야 한다면 루프를 고려할 수 있습니다. 하지만 R에서 루프를 사용할 때는 결과값을 담을 객체를 미리 생성하는 것이 성능상 매우 중요합니다.
varNames <- names(adsl_saf)
counts <- vector("integer", length(varNames)) # 결과 저장용 객체 미리 생성
for(i in 1:length(varNames)){
counts[i] <- sum(!is.na(adsl_saf[[varNames[i]]]))
}
names(counts) <- varNames
head(counts)STUDYID USUBJID SUBJID SITEID SITEGR1 ARM
254 254 254 254 254 254
이 방식은 SAS 사용자에게 익숙할 수 있지만, 결과가 데이터 프레임이 아닌 벡터로 반환되어 후속 작업(select() 등)이 불편할 수 있습니다.
기본 R의 apply 함수군은 입력을 받아 각 요소에 함수를 적용합니다.
apply(): 배열이나 행렬의 행(MARGIN=1) 또는 열(MARGIN=2) 단위 처리lapply(): 리스트(List) 요소별 처리 (결과는 리스트)sapply(): 결과를 벡터나 행렬로 단순화(Simplify)하여 반환# 데이터 프레임의 각 열에 함수 적용
sapply(adsl_saf, function(x) sum(!is.na(x))) %>% head()STUDYID USUBJID SUBJID SITEID SITEGR1 ARM
254 254 254 254 254 254
{tidyverse}는 더 직관적이고 예측 가능한 반복 처리 방법을 제공합니다.
across()를 이용한 열 단위 처리summarise()나 mutate() 내부에서 여러 열에 동시에 함수를 적용할 때 사용합니다.
# 모든 수치형 변수의 중앙값 계산
adsl_saf %>%
summarise(across(where(is.numeric), median, na.rm = TRUE))Warning: There was 1 warning in `summarise()`.
ℹ In argument: `across(where(is.numeric), median, na.rm = TRUE)`.
Caused by warning:
! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
Supply arguments directly to `.fns` through an anonymous function instead.
# Previously
across(a:b, mean, na.rm = TRUE)
# Now
across(a:b, \(x) mean(x, na.rm = TRUE))
TRT01PN TRT01AN TRTDUR AVGDD CUMDOSE AGE AGEGR1N RACEN BMIBL HEIGHTBL
1 54 54 133.5 54 2322 77 2 1 24.2 162.85
WEIGHTBL EDUCLVL DURDIS VISNUMEN MMSETOT
1 66.7 12 36.25 11 19
starts_with(), contains(), where(is.character) 등 다양한 조건으로 대상 열을 선택할 수 있어 매우 유연합니다.
rowwise()를 이용한 행 단위 처리데이터의 각 행별로 계산이 필요할 때 사용합니다.
# 각 피험자 행별로 수치형 데이터 중 결측치가 몇 개인지 계산
adsl_saf %>%
rowwise(SUBJID) %>%
summarise(missing_cnt = sum(is.na(c_across(where(is.numeric))))) %>%
head()`summarise()` has grouped output by 'SUBJID'. You can override using the
`.groups` argument.
# A tibble: 6 × 2
# Groups: SUBJID [6]
SUBJID missing_cnt
<chr> <int>
1 1015 0
2 1023 0
3 1028 0
4 1033 0
5 1034 0
6 1047 0
{purrr}와 map() 함수{purrr} 패키지는 리스트 형태의 데이터를 다루는 강력한 도구입니다. map() 함수는 리스트의 각 요소에 함수를 적용합니다.
# 치료군별로 데이터를 나누어 리스트로 만든 뒤, 각각 요약 정보 확인
adsl_saf %>%
split(.$TRT01A) %>%
map(~ skimr::skim(.x))$Placebo
── Data Summary ────────────────────────
Values
Name .x
Number of rows 86
Number of columns 48
_______________________
Column type frequency:
character 28
Date 5
numeric 15
________________________
Group variables None
── Variable type: character ────────────────────────────────────────────────────
skim_variable n_missing complete_rate min max empty n_unique whitespace
1 STUDYID 0 1 12 12 0 1 0
2 USUBJID 0 1 11 11 0 86 0
3 SUBJID 0 1 4 4 0 86 0
4 SITEID 0 1 3 3 0 16 0
5 SITEGR1 0 1 3 3 0 11 0
6 ARM 0 1 7 7 0 1 0
7 TRT01P 0 1 7 7 0 1 0
8 TRT01A 0 1 7 7 0 1 0
9 AGEGR1 0 1 3 5 0 3 0
10 AGEU 0 1 5 5 0 1 0
11 RACE 0 1 5 25 0 2 0
12 SEX 0 1 1 1 0 2 0
13 ETHNIC 0 1 18 22 0 2 0
14 SAFFL 0 1 1 1 0 1 0
15 ITTFL 0 1 1 1 0 1 0
16 EFFFL 0 1 1 1 0 2 0
17 COMP8FL 0 1 1 1 0 2 0
18 COMP16FL 0 1 1 1 0 2 0
19 COMP24FL 0 1 1 1 0 2 0
20 DISCONFL 0 1 0 1 58 2 0
21 DSRAEFL 0 1 0 1 78 2 0
22 DTHFL 0 1 0 1 84 2 0
23 BMIBLGR1 0 1 3 6 0 3 0
24 DURDSGR1 0 1 3 4 0 2 0
25 RFSTDTC 0 1 10 10 0 81 0
26 RFENDTC 0 1 10 10 0 80 0
27 DCDECOD 0 1 5 27 0 9 0
28 DCREASCD 0 1 5 18 0 10 0
── Variable type: Date ─────────────────────────────────────────────────────────
skim_variable n_missing complete_rate min max median
1 TRTSDT 0 1 2012-07-09 2014-09-02 2013-07-12
2 TRTEDT 0 1 2012-09-01 2015-03-05 2013-11-22
3 DISONSDT 0 1 1998-06-13 2013-04-08 2010-08-17
4 VISIT1DT 0 1 2012-07-06 2014-08-29 2013-07-04
5 RFENDT 0 1 2012-09-02 2015-03-05 2013-11-22
n_unique
1 81
2 80
3 85
4 80
5 80
── Variable type: numeric ──────────────────────────────────────────────────────
skim_variable n_missing complete_rate mean sd p0 p25 p50 p75
1 TRT01PN 0 1 0 0 0 0 0 0
2 TRT01AN 0 1 0 0 0 0 0 0
3 TRTDUR 0 1 149. 60.3 7 131 182 183
4 AVGDD 0 1 0 0 0 0 0 0
5 CUMDOSE 0 1 0 0 0 0 0 0
6 AGE 0 1 75.2 8.59 52 69.2 76 81.8
7 AGEGR1N 0 1 2.19 0.695 1 2 2 3
8 RACEN 0 1 1.09 0.292 1 1 1 1
9 BMIBL 0 1 23.6 3.67 15.1 21.2 23.4 25.6
10 HEIGHTBL 0 1 163. 11.5 137. 154 163. 171.
11 WEIGHTBL 0 1 62.8 12.8 34 53.6 60.6 74.2
12 EDUCLVL 0 1 12.6 2.95 6 12 12 14.8
13 DURDIS 0 1 42.6 30.2 7.2 24.3 35.3 50.1
14 VISNUMEN 0 1 10.7 2.38 4 11 12 12
15 MMSETOT 0 1 18.0 4.27 10 15 19.5 22
p100 hist
1 0 ▁▁▇▁▁
2 0 ▁▁▇▁▁
3 210 ▂▁▁▁▇
4 0 ▁▁▇▁▁
5 0 ▁▁▇▁▁
6 89 ▁▃▇▇▇
7 3 ▂▁▇▁▆
8 2 ▇▁▁▁▁
9 33.3 ▂▆▇▃▂
10 185. ▂▇▇▇▅
11 86.2 ▂▇▇▇▇
12 21 ▂▇▂▃▁
13 183. ▇▃▁▁▁
14 12 ▁▁▁▁▇
15 23 ▃▃▃▃▇
$`Xanomeline High Dose`
── Data Summary ────────────────────────
Values
Name .x
Number of rows 84
Number of columns 48
_______________________
Column type frequency:
character 28
Date 5
numeric 15
________________________
Group variables None
── Variable type: character ────────────────────────────────────────────────────
skim_variable n_missing complete_rate min max empty n_unique whitespace
1 STUDYID 0 1 12 12 0 1 0
2 USUBJID 0 1 11 11 0 84 0
3 SUBJID 0 1 4 4 0 84 0
4 SITEID 0 1 3 3 0 15 0
5 SITEGR1 0 1 3 3 0 11 0
6 ARM 0 1 20 20 0 1 0
7 TRT01P 0 1 20 20 0 1 0
8 TRT01A 0 1 20 20 0 1 0
9 AGEGR1 0 1 3 5 0 3 0
10 AGEU 0 1 5 5 0 1 0
11 RACE 0 1 5 32 0 3 0
12 SEX 0 1 1 1 0 2 0
13 ETHNIC 0 1 18 22 0 2 0
14 SAFFL 0 1 1 1 0 1 0
15 ITTFL 0 1 1 1 0 1 0
16 EFFFL 0 1 1 1 0 2 0
17 COMP8FL 0 1 1 1 0 2 0
18 COMP16FL 0 1 1 1 0 2 0
19 COMP24FL 0 1 1 1 0 2 0
20 DISCONFL 0 1 0 1 27 2 0
21 DSRAEFL 0 1 0 1 44 2 0
22 DTHFL 0 1 0 0 84 1 0
23 BMIBLGR1 0 1 3 6 0 3 0
24 DURDSGR1 0 1 3 4 0 2 0
25 RFSTDTC 0 1 10 10 0 78 0
26 RFENDTC 0 1 10 10 0 79 0
27 DCDECOD 0 1 9 27 0 7 0
28 DCREASCD 0 1 9 18 0 8 0
── Variable type: Date ─────────────────────────────────────────────────────────
skim_variable n_missing complete_rate min max median
1 TRTSDT 0 1 2012-07-20 2014-07-01 2013-06-04
2 TRTEDT 0 1 2012-10-20 2014-12-30 2013-08-22
3 DISONSDT 0 1 2001-07-15 2013-02-04 2010-06-04
4 VISIT1DT 0 1 2012-07-10 2014-06-24 2013-05-28
5 RFENDT 0 1 2012-10-23 2014-12-30 2013-08-25
n_unique
1 78
2 78
3 82
4 75
5 79
── Variable type: numeric ──────────────────────────────────────────────────────
skim_variable n_missing complete_rate mean sd p0 p25 p50
1 TRT01PN 0 1 81 0 81 81 81
2 TRT01AN 0 1 81 0 81 81 81
3 TRTDUR 0 1 99.4 70.6 1 37.8 76.5
4 AVGDD 0 1 71.6 8.11 54 70.2 75.1
5 CUMDOSE 0 1 7551 5531. 54 2646 5778
6 AGE 0 1 74.4 7.89 56 70.8 76
7 AGEGR1N 0 1 2.08 0.585 1 2 2
8 RACEN 0 1 1.17 0.618 1 1 1
9 BMIBL 0 1 25.3 4.16 13.7 22.7 24.8
10 HEIGHTBL 0 1 166. 10.1 146. 158. 165.
11 WEIGHTBL 0 1 70.0 14.7 41.7 57.0 69.2
12 EDUCLVL 0 1 12.5 2.92 6 11.8 12
13 DURDIS 0 1 40.5 24.7 2.2 23.9 36.0
14 VISNUMEN 0 1 8.98 2.87 4 7 9
15 MMSETOT 0 1 18.5 4.16 10 16 20
p75 p100 hist
1 81 81 ▁▁▇▁▁
2 81 81 ▁▁▇▁▁
3 181. 200 ▆▅▂▁▇
4 76.9 78.6 ▂▁▁▂▇
5 13959 15417 ▆▅▂▁▇
6 80 88 ▂▂▆▇▃
7 2 3 ▂▁▇▁▂
8 1 6 ▇▁▁▁▁
9 27.8 34.5 ▁▃▇▅▂
10 173. 190. ▅▅▇▅▁
11 80.3 108 ▅▆▇▃▂
12 15 20 ▂▂▇▃▁
13 52.4 135 ▇▇▃▁▁
14 12 12 ▃▃▂▃▇
15 22 24 ▃▂▅▆▇
$`Xanomeline Low Dose`
── Data Summary ────────────────────────
Values
Name .x
Number of rows 84
Number of columns 48
_______________________
Column type frequency:
character 28
Date 5
numeric 15
________________________
Group variables None
── Variable type: character ────────────────────────────────────────────────────
skim_variable n_missing complete_rate min max empty n_unique whitespace
1 STUDYID 0 1 12 12 0 1 0
2 USUBJID 0 1 11 11 0 84 0
3 SUBJID 0 1 4 4 0 84 0
4 SITEID 0 1 3 3 0 17 0
5 SITEGR1 0 1 3 3 0 11 0
6 ARM 0 1 19 19 0 1 0
7 TRT01P 0 1 19 19 0 1 0
8 TRT01A 0 1 19 19 0 1 0
9 AGEGR1 0 1 3 5 0 3 0
10 AGEU 0 1 5 5 0 1 0
11 RACE 0 1 5 25 0 2 0
12 SEX 0 1 1 1 0 2 0
13 ETHNIC 0 1 18 22 0 2 0
14 SAFFL 0 1 1 1 0 1 0
15 ITTFL 0 1 1 1 0 1 0
16 EFFFL 0 1 1 1 0 2 0
17 COMP8FL 0 1 1 1 0 2 0
18 COMP16FL 0 1 1 1 0 2 0
19 COMP24FL 0 1 1 1 0 2 0
20 DISCONFL 0 1 0 1 25 2 0
21 DSRAEFL 0 1 0 1 40 2 0
22 DTHFL 0 1 0 1 83 2 0
23 BMIBLGR1 0 1 3 6 0 3 0
24 DURDSGR1 0 1 3 4 0 2 0
25 RFSTDTC 0 1 10 10 0 77 0
26 RFENDTC 0 1 10 10 0 82 0
27 DCDECOD 0 1 5 27 0 7 0
28 DCREASCD 0 1 5 18 0 7 0
── Variable type: Date ─────────────────────────────────────────────────────────
skim_variable n_missing complete_rate min max median
1 TRTSDT 0 1 2012-07-22 2014-05-22 2013-05-23
2 TRTEDT 0 1 2012-08-28 2014-11-20 2013-08-15
3 DISONSDT 0 1 2002-07-16 2013-09-16 2009-10-01
4 VISIT1DT 0 1 2012-07-08 2014-05-10 2013-05-13
5 RFENDT 0 1 2012-09-01 2014-11-20 2013-08-17
n_unique
1 77
2 80
3 83
4 77
5 82
── Variable type: numeric ──────────────────────────────────────────────────────
skim_variable n_missing complete_rate mean sd p0 p25 p50
1 TRT01PN 0 1 54 0 54 54 54
2 TRT01AN 0 1 54 0 54 54 54
3 TRTDUR 0 1 99.0 68.2 2 36.8 82.5
4 AVGDD 0 1 54 0 54 54 54
5 CUMDOSE 0 1 5347. 3680. 108 1984. 4455
6 AGE 0 1 75.7 8.29 51 71 77.5
7 AGEGR1N 0 1 2.25 0.618 1 2 2
8 RACEN 0 1 1.07 0.259 1 1 1
9 BMIBL 1 0.988 25.1 4.27 17.7 22.2 24.3
10 HEIGHTBL 0 1 163. 10.4 136. 158. 163.
11 WEIGHTBL 1 0.988 67.3 14.1 45.4 56.0 64.9
12 EDUCLVL 0 1 13.2 4.15 3 12 12
13 DURDIS 0 1 48.7 29.6 7.8 26.4 40.2
14 VISNUMEN 0 1 9.01 2.87 4 7 9.5
15 MMSETOT 0 1 17.9 4.22 10 14 18
p75 p100 hist
1 54 54 ▁▁▇▁▁
2 54 54 ▁▁▇▁▁
3 182. 212 ▇▆▃▂▇
4 54 54 ▁▁▇▁▁
5 9801 11448 ▇▆▃▂▇
6 82 88 ▂▁▅▇▇
7 3 3 ▂▁▇▁▅
8 1 2 ▇▁▁▁▁
9 27.8 40.1 ▅▇▅▁▁
10 170. 196. ▁▆▇▃▁
11 77.4 106. ▇▇▇▂▂
12 16 24 ▂▂▇▅▁
13 66.4 131. ▇▇▅▂▁
14 12 12 ▅▂▂▆▇
15 22 24 ▅▅▆▆▇
반복 작업이 매우 많고 시간이 오래 걸린다면, 여러 프로세서를 사용하는 병렬 처리를 고려할 수 있습니다. {multidplyr}, {furrr}, {foreach} 등의 패키지가 활용됩니다.
미니 프로젝트 7에서 만든 adsl_counts 함수를 활용해 보세요. adsl_saf 데이터를 인종(RACE)별로 나누어(split) 각 그룹에 대해 치료군별 성별 빈도를 산출해 보세요.