iBetter Books
수정

Ch 05. facet으로 다중 패널 만들기

한 차트에 여러 그룹을 색상으로 구분하면 비교가 가능하지만 한계가 있습니다. 그룹이 많아지면 색상이 뒤엉키고, 각 그룹 내의 분포를 세밀하게 보기 어렵습니다.

facet은 이 문제를 해결합니다. 하나의 차트를 그룹별로 나누어 소 차트(패널)를 만듭니다. 같은 축 스케일로 정렬되어 있어서 그룹 간 비교가 직관적입니다.

facet_wrap — 변수 하나로 패널 분할

가장 자주 쓰이는 facet 함수입니다. 하나의 범주형 변수를 기준으로 패널을 만들고, 자동으로 배치합니다.

library(tidyverse)

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(color = "steelblue", alpha = 0.6) +
  facet_wrap(~ class) +
  labs(
    title = "클래스별 배기량과 연비",
    x     = "엔진 배기량 (리터)",
    y     = "고속도로 연비 (mpg)"
  ) +
  theme_minimal()

~ class는 "class 변수를 기준으로 나누겠다"는 공식입니다. 차량 클래스 수(7개)만큼 패널이 만들어지고, ggplot2가 자동으로 행과 열을 배치합니다.

열 수 지정

ncol로 한 행에 몇 개의 패널을 배치할지 지정합니다.

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(color = "steelblue", alpha = 0.6) +
  geom_smooth(method = "lm", se = FALSE, color = "tomato", linewidth = 0.7) +
  facet_wrap(~ class, ncol = 3) +
  labs(
    title = "클래스별 배기량과 연비 (3열 배치)",
    x     = "배기량 (리터)",
    y     = "고속도로 연비 (mpg)"
  ) +
  theme_minimal()

ncol = 3이면 3열로 정렬됩니다. nrow로 행 수를 지정할 수도 있습니다.

scales 인수 — 축 스케일 제어

기본적으로 모든 패널은 동일한 축 범위를 공유합니다. 그룹별 데이터 범위가 크게 다를 때는 scales 인수로 조정합니다.

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(alpha = 0.6) +
  facet_wrap(~ class, scales = "free") +
  labs(
    title = "클래스별 배기량과 연비 (축 자유 조정)",
    x     = "배기량 (리터)",
    y     = "고속도로 연비 (mpg)"
  ) +
  theme_minimal()

scales 옵션 정리.

의미
"fixed" 모든 패널이 동일한 축 (기본값)
"free" X, Y축 모두 각 패널에 맞게 조정
"free_x" X축만 자유, Y축 고정
"free_y" Y축만 자유, X축 고정

scales = "free"는 패널 내 분포를 자세히 보고 싶을 때 유용합니다. 단, 그룹 간 절대적인 크기 비교는 어려워집니다. 목적에 따라 선택합니다.

facet_grid — 두 변수로 격자 패널

행과 열에 각각 다른 변수를 배치해 격자 형태의 패널을 만듭니다.

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(alpha = 0.5, color = "steelblue") +
  facet_grid(drv ~ cyl) +
  labs(
    title = "구동 방식 × 실린더 수별 배기량과 연비",
    x     = "배기량 (리터)",
    y     = "고속도로 연비 (mpg)"
  ) +
  theme_minimal()

drv ~ cyl는 "행에 drv, 열에 cyl"을 배치하겠다는 뜻입니다.

  • 행: drv (구동 방식 — f, r, 4) 3가지
  • 열: cyl (실린더 수 — 4, 5, 6, 8) 4가지

총 12개의 패널이 격자로 배치됩니다. 빈 조합은 점이 없는 빈 패널로 표시됩니다.

행 또는 열 중 하나만 변수로 쓰려면 나머지에 .을 씁니다.

# 행만 변수로 분할
facet_grid(drv ~ .)

# 열만 변수로 분할
facet_grid(. ~ cyl)

facet_wrap vs facet_grid

항목 facet_wrap facet_grid
변수 수 1개 1~2개
패널 배치 자동(ncol/nrow 조정 가능) 격자 고정
빈 조합 표시하지 않음 빈 패널 표시
사용 목적 단일 범주 비교 두 범주의 교차 비교

두 변수의 조합 효과를 보고 싶다면 facet_grid, 한 변수를 간단히 나누어 보고 싶다면 facet_wrap이 적합합니다.

facet과 색상 함께 활용

facet으로 나누면서 색상으로 또 다른 변수를 표현하면 더 많은 정보를 담을 수 있습니다.

ggplot(mpg, aes(x = displ, y = hwy, color = drv)) +
  geom_point(size = 2, alpha = 0.7) +
  facet_wrap(~ year, ncol = 2) +
  scale_color_brewer(palette = "Set2",
                     labels  = c("4" = "사륜", "f" = "전륜", "r" = "후륜")) +
  labs(
    title = "연도별 배기량과 연비 비교",
    x     = "배기량 (리터)",
    y     = "고속도로 연비 (mpg)",
    color = "구동 방식"
  ) +
  theme_minimal() +
  theme(legend.position = "bottom")

연도별로 패널을 나누고, 구동 방식을 색상으로 구분합니다. 1999년과 2008년의 패턴 변화를 비교할 수 있습니다.

그룹별 분포 비교 — facet 실전 예시

diamonds 데이터에서 컷 품질과 색상 등급에 따른 가격 분포를 비교합니다.

ggplot(diamonds, aes(x = price, fill = cut)) +
  geom_histogram(binwidth = 1000, alpha = 0.8) +
  facet_wrap(~ color, ncol = 4) +
  scale_fill_brewer(palette = "Set2") +
  scale_x_continuous(labels = scales::comma) +
  labs(
    title = "다이아몬드 색상 등급별 가격 분포",
    x     = "가격 (달러)",
    y     = "빈도",
    fill  = "컷 품질"
  ) +
  theme_minimal() +
  theme(
    legend.position = "bottom",
    axis.text.x     = element_text(angle = 45, hjust = 1, size = 7)
  )

scales::comma는 X축 숫자에 쉼표(1,000 단위)를 붙여줍니다. scales 패키지는 tidyverse에 포함되어 있습니다.

facet은 "같은 질문을 여러 그룹에 동시에 던지는" 도구입니다. 한 차트로는 보기 어려운 그룹 간 패턴 차이를 직관적으로 드러냅니다. 다음 챕터에서는 완성한 차트를 파일로 저장하고, 여러 차트를 하나로 조합하는 방법을 살펴봅니다.