data.table 패키지 사용기 02: 데이터 저장 포맷 (.fst & .rds)

KNHIS 확장자 파이프라인 정착기
R
Author

Sungjun Park

Published

2026-05-31

개요

건강보험공단(KNHIS)의 t20, t30, t40 같이 수천만 건에 달하는 대용량 테이블을 다루다 보면, R이 뻗어버리는 경험을 종종 하게 된다. 여러 시행착오 끝에 우선 현재까지의 정리된 생각으로 파이프라인을 기록해본다.

외부 원본(.sas7bdat)은 최초 1회만 읽고 무조건 .fst로 변환하여 메인 전처리에 사용한다. 전처리 과정에서 생성되는 복잡한 리스트(list) 객체나 모형은 .rds로 보관한다.

각 확장자의 스펙을 간단히 표로 요약하면 아래와 같다.

확장자 포맷 주력 용도 핵심 장점 치명적 단점
.rds 복잡한 객체 보관용 list 등 R 고유 구조체 100% 보존 타 언어 호환 불가
.fst 수천만 건 분석용 미친 속도, 부분 열기(Random Access) data.table만 지원
.csv 외부 공유용 압도적인 호환성 (Python, Excel) 용량 큼, 데이터 속성(Factor 등) 증발
.sas7bdat 원본 수령용 공공기관 제공 표준 포맷 R 환경에서 읽기 속도 최악

기본적으로 .fst

t20이나 t40 같은 무식한 크기의 원본 테이블을 다룰 때는 무조건 .fst다. SAS 파일을 최초에 1회만 .fst로 변환해 두자.

t40 <- read_fst(
  "fst/t40_target_mod.fst", 
  columns = c(
    "INDI_DSCM_NO", 
    "MCEX_SICK_SYM", 
    "SICK_CLSF_TYPE", 
    "MDCARE_STRT_DT", 
    "FORM_CD"
  ),
  as.data.table = TRUE
) %>% 
  .[SICK_CLSF_TYPE %in% c(1, 2)]

.fst는 수백 개의 열 중 내가 원하는 열만 하드디스크에서 콕 집어 메모리에 올리는 부분 열기(Random Access) 가 완벽하게 지원된다. 멀티코어를 활용해 초당 기가바이트 단위로 데이터를 뿜어주기 때문에 수천만 건의 데이터를 다룰 때 완벽한 해답이 된다.

ID는 .rds

데이터를 만지다 보면 필연적으로 2차원 표(Dataframe)의 형태를 벗어나는 순간이 종종 있다.

과거 병력(Medical History) 조건에 맞는 환자 ID(INDI_DSCM_NO)를 뽑아내는 부분을 보자.

covid_medhis <- list()

covid_medhis$dia_id <- c( # 과거 1년 중 입원 1회기록 
  t40 %>% 
    .[like(MCEX_SICK_SYM, covcod_medhis$dia) & FORM_CD == 2] %>% 
    .[, .(INDI_DSCM_NO, hdate = MDCARE_STRT_DT)] %>% 
    .[
      data_first_pre[, .(INDI_DSCM_NO, MDCARE_STRT_DT)] %>% unique(), 
      on = .(INDI_DSCM_NO)
    ] %>% 
    .[
      is.na(hdate) == FALSE 
      & as.integer(ymd(MDCARE_STRT_DT)) - as.integer(ymd(hdate)) >= 0 
      & as.integer(ymd(MDCARE_STRT_DT)) - as.integer(ymd(hdate)) <= 364 
    ] %>% 
    .[, .N, keyby = .(INDI_DSCM_NO)] %>% 
    .[N >= 1] %>% 
    .[["INDI_DSCM_NO"]] %>% 
    unique(),
  
  # ... (외래 조건 생략) ...
)

covid_medhis$hypten_id <- c( # 고혈압 ... )
covid_medhis$ckd.esrd_id <- c( # 만성신장질환 ... )

객체 covid_medhis는 단순한 표가 아니라 당뇨, 고혈압, 만성신장질환 등 각각의 환자 ID 벡터(Vector)들을 줄줄이 품고 있는 거대한 리스트(List) 객체다.

이런 형태의 데이터는 .fst.csv로는 애초에 저장이 불가능하다. 반면 .rds는 이런 R의 고유한 구조체를 타임캡슐처럼 100% 원본 그대로 박제해서 용량까지 극적으로 압축해 준다.

saveRDS(covid_medhis, "data/covid_medhis.rds")

나중에 코호트 제외조건을 걸거나 공변량을 만들 떄 활용한다.

모델의 세부조건이 바뀔 것을 염두에 두면, 매번 위 코드를 돌리기보다는 .rds 파일로 저장해놓고 불러오는게 효율적인 것 같다.