{ggplot2}를 이용한 시각화 기초

Author

Mike K Smith

Published

February 16, 2023

이 미니 프로젝트에서는 R의 대표적인 시각화 패키지인 {ggplot2}를 사용하여 데이터를 시각화하는 방법을 배웁니다. {ggplot2}는 레이어(Layer) 구조를 기반으로 하며, 수많은 확장 패키지를 통해 기능을 무한히 확장할 수 있다는 장점이 있습니다.

목표

  • ggplot() 함수와 aes() (Aesthetic mapping) 이해
  • geom_point(), geom_line()을 이용한 산점도 및 선 그래프 생성
  • labs()를 이용한 제목 및 축 레이블 설정
  • facet_wrap()을 이용한 그룹별 분할 그래프 생성

데이터 소스

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

1. 데이터셋 준비

CDISC의 분석 활력 징후(ADVS) 데이터를 사용합니다. 여기서는 심박수(Pulse Rate) 측정치에 집중하며, 치료 시작 후 경과 주차를 나타내는 StudyWeek 변수를 새로 계산합니다.

ADVS <- import(file = "./data/advs.xpt")
PR <- ADVS %>%
  filter(PARAMN == 3) %>% # 심박수 필터링
  filter(VISITNUM > 3) %>% # 치료 기간 이후 데이터
  mutate(StudyWeek = floor(ADY/7))

2. ggplot 기본 구조

ggplot() 함수는 그래프를 그릴 빈 “캔버스”를 생성합니다. data 인수는 사용할 데이터셋을, mapping 인수는 데이터의 열을 그래프의 시각적 요소(축, 색상 등)에 어떻게 연결할지(aes() 함수 사용) 정의합니다.

# x축을 연구일(ADY), y축을 측정치(AVAL)로 설정
ggplot(data = PR, mapping = aes(x = ADY, y = AVAL)) 

: 학습 초기에는 mapping = aes(...)와 같이 인수를 명시적으로 작성하는 것을 권장합니다.

3. 기하학적 객체(Geom) 추가

산점도를 그리려면 위 문구에 + geom_point()를 추가합니다. + 연산자는 레이어를 쌓는 역할을 하며, 반드시 줄 끝에 위치해야 합니다.

ggplot(data = PR, mapping = aes(x = ADY, y = AVAL)) + 
  geom_point()
Warning: Removed 3 rows containing missing values or values outside the scale range
(`geom_point()`).

4. 데이터 그룹화

치료군(TRTA)별로 데이터를 구분해 보겠습니다. aes() 내부에 group, colour, shape 등을 지정할 수 있습니다.

ggplot(data = PR, mapping = aes(x = ADY, y = AVAL, 
                                colour = TRTA, shape = TRTA)) + 
  geom_point()
Warning: Removed 3 rows containing missing values or values outside the scale range
(`geom_point()`).

5. 개별 피험자별 선 연결 (스파게티 플롯)

동일한 피험자의 측정치를 선으로 연결하여 변화 추이를 확인하는 “스파게티 플롯 (Spaghetti Plot)”을 그려봅니다. 피험자 식별자인 USUBJIDgroup에 매핑합니다.

plot1 <- PR %>%
  ggplot(mapping = aes(x = ADY, y = AVAL, group = USUBJID)) +
  geom_line(alpha = 0.5) # 선의 투명도 조절

plot1
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_line()`).

6. 레이블 및 테마 설정

labs() 함수로 제목과 축 레이블을 한글로 수정하고, theme_bw()를 적용하여 깔끔한 테마로 변경합니다.

plot2 <- plot1 + 
  labs(title = "시간에 따른 심박수 변화",
       subtitle = "피험자별 개별 프로파일 (스파게티 플롯)",
       x = "연구일 (Study Day)",
       y = "심박수 (bpm)",
       caption = paste("생성일:", Sys.Date()))

plot3 <- plot2 + theme_bw()
plot3
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_line()`).

7. 패싯(Facet)을 이용한 그룹별 분할

치료군(TRTA)별로 그래프를 나누어 보고 싶을 때 facet_wrap()을 사용합니다.

plot4 <- plot3 +
  facet_wrap(~ TRTA)
plot4
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_line()`).

8. 결과 저장

ggsave()를 사용하여 결과물을 이미지 파일로 저장할 수 있습니다.

# ggsave("heart_rate_plot.png", plot = plot4, width = 8, height = 6, units = "in")

9. 요약 통계(중앙값) 추가

개별 데이터 위에 전체 중앙값의 흐름을 겹쳐 그릴 수 있습니다. stat_summary()를 사용합니다.

# 주차별 데이터를 위해 필터링된 데이터셋 생성
dataWeeks <- PR %>%
  filter(StudyWeek %in% c(0, 5, 10, 15, 20, 25, 30)) %>%
  mutate(ADY = StudyWeek * 7)

plot3 + 
  stat_summary(data = dataWeeks,
               mapping = aes(x = ADY, group = NULL), 
               fun = median, 
               geom = "line", 
               colour = "red", 
               linewidth = 1)
Warning: Removed 1 row containing non-finite outside the scale range
(`stat_summary()`).
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_line()`).

10. 축 범위 조정 (Zooming)

데이터를 삭제하지 않고 특정 영역을 확대해서 보려면 coord_cartesian()을 사용합니다.

plot4 + 
  coord_cartesian(ylim = c(50, 120))
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_line()`).

챌린지

수축기 혈압(Systolic BP), 이완기 혈압(Diastolic BP), 체온(Temperature) 등에 대해서도 동일한 과정을 수행해 보세요. 데이터의 특성에 맞게 축 레이블을 수정하는 것을 잊지 마세요.