iBetter Books
수정

Ch 02. 기본 차트 (막대, 선, 산점도)

어떤 데이터를 어떤 차트로 표현할지 결정하는 일은 생각보다 중요합니다. 막대그래프로 봐야 할 것을 꺾은선으로 그리거나, 산점도로 봐야 할 것을 막대로 그리면 데이터가 말하려는 내용이 흐려집니다.

이 챕터에서는 가장 많이 쓰이는 세 가지 차트 — 막대, 선, 산점도 — 를 만들고, 각각 어떤 상황에 어울리는지 함께 생각해봅니다.

막대그래프 — geom_bar vs geom_col

막대그래프는 범주형 데이터의 크기를 비교할 때 씁니다. ggplot2에는 막대를 그리는 함수가 두 개 있습니다. geom_bar()geom_col()입니다. 헷갈리기 쉬운 두 함수의 차이를 먼저 짚습니다.

geom_bar() — 빈도를 자동으로 셉니다.

library(tidyverse)

ggplot(mpg, aes(x = class)) +
  geom_bar()

mpg 데이터에서 각 차량 클래스가 몇 번 등장하는지 ggplot2가 직접 셉니다. y 값을 지정하지 않아도 됩니다. 내부적으로 stat = "count"가 작동합니다.

geom_col() — 이미 계산된 값을 막대로 그립니다.

# 클래스별 평균 고속도로 연비 계산
mpg_summary <- mpg |>
  group_by(class) |>
  summarise(mean_hwy = mean(hwy))

ggplot(mpg_summary, aes(x = class, y = mean_hwy)) +
  geom_col()

미리 계산한 mean_hwy 값을 그대로 막대 높이로 씁니다. x와 y 모두 지정해야 합니다.

언제 어떤 것을 쓸까.

상황 함수
각 범주의 개수를 세고 싶다 geom_bar()
직접 계산한 값을 막대로 표현한다 geom_col()

막대 색상과 정렬

색상을 범주에 따라 바꾸려면 fill을 매핑합니다.

ggplot(mpg, aes(x = class, fill = class)) +
  geom_bar() +
  theme_minimal() +
  labs(title = "차량 클래스별 대수", x = "클래스", y = "대수") +
  theme(legend.position = "none")

fill은 막대 안쪽 색입니다. color는 테두리 색입니다. 범례가 불필요할 때는 theme(legend.position = "none")으로 숨깁니다.

막대를 값 기준으로 정렬하면 비교가 쉬워집니다. fct_reorder()를 씁니다.

ggplot(mpg, aes(x = fct_reorder(class, class, length), fill = class)) +
  geom_bar() +
  coord_flip() +
  theme_minimal() +
  labs(title = "차량 클래스별 대수 (내림차순)", x = "클래스", y = "대수") +
  theme(legend.position = "none")

fct_reorder(class, class, length)는 class를 빈도 기준으로 정렬합니다. coord_flip()으로 가로 방향으로 바꾸면 클래스 이름이 읽기 편해집니다.

누적 막대그래프와 그룹 막대그래프

position 인수로 막대를 쌓거나 나란히 놓을 수 있습니다.

# 누적 막대 (기본값)
ggplot(mpg, aes(x = class, fill = drv)) +
  geom_bar(position = "stack") +
  theme_minimal() +
  labs(title = "클래스별 구동 방식 분포 (누적)")
# 그룹 막대 (나란히)
ggplot(mpg, aes(x = class, fill = drv)) +
  geom_bar(position = "dodge") +
  theme_minimal() +
  labs(title = "클래스별 구동 방식 분포 (그룹)")
# 비율 막대 (합계 = 100%)
ggplot(mpg, aes(x = class, fill = drv)) +
  geom_bar(position = "fill") +
  theme_minimal() +
  labs(title = "클래스별 구동 방식 비율", y = "비율")

position = "fill"은 각 막대를 100%로 맞춥니다. 구성 비율을 비교할 때 유용합니다.

꺾은선 그래프 — geom_line

꺾은선 그래프는 시간에 따른 변화, 즉 추세를 볼 때 씁니다. 순서가 있는 연속형 데이터에 어울립니다.

economics 데이터셋을 사용합니다. 미국 경제 지표 월별 시계열 데이터입니다.

glimpse(economics)
Rows: 574
Columns: 6
$ date     <date> 1967-07-01, 1967-08-01, 1967-09-01, 1967-10-01, 1967-11-01…
$ pce      <dbl> 507.4, 510.5, 516.3, 512.9, 518.1, 525.8, 531.5, 534.2, 54…
$ pop      <dbl> 198712, 198911, 199113, 199311, 199498, 199657, 199808, 1999…
$ psavert  <dbl> 12.5, 12.5, 11.7, 12.5, 12.5, 12.1, 11.7, 12.2, 11.6, 12.2…
$ uempmed  <dbl> 4.5, 4.7, 4.6, 4.9, 4.7, 4.8, 5.1, 4.5, 4.1, 4.6, 4.4, 4.…
$ unemploy <dbl> 2944, 2945, 2958, 3143, 3066, 3018, 2878, 3001, 2877, 2709,…

실업자 수(unemploy)의 추이를 그려봅니다.

ggplot(economics, aes(x = date, y = unemploy)) +
  geom_line(color = "steelblue", linewidth = 0.8) +
  labs(
    title = "미국 실업자 수 추이 (1967~2015)",
    x     = "날짜",
    y     = "실업자 수 (천 명)"
  ) +
  theme_minimal()

금융 위기 시점(2008~2009)에 실업자 수가 급등하는 모습이 뚜렷하게 보입니다.

여러 선 그리기

그룹별로 다른 선을 그리려면 group이나 color를 변수에 매핑합니다.

# 저축률과 개인 소비 지출의 추이 비교
economics_long <- economics |>
  select(date, psavert, uempmed) |>
  pivot_longer(cols = c(psavert, uempmed),
               names_to  = "variable",
               values_to = "value")

ggplot(economics_long, aes(x = date, y = value, color = variable)) +
  geom_line(linewidth = 0.7) +
  labs(
    title = "저축률 vs 실업 중위기간 비교",
    x     = "날짜",
    y     = "값",
    color = "지표"
  ) +
  theme_minimal()

pivot_longer()로 데이터를 긴 형태로 바꾼 뒤 color = variable을 매핑합니다. 두 지표가 한 차트에 서로 다른 색으로 그려집니다.

선과 점을 함께 그리기

데이터 포인트를 강조하고 싶을 때는 geom_line()geom_point()를 함께 씁니다.

# 연도별 평균 고속도로 연비
mpg_year <- mpg |>
  group_by(year) |>
  summarise(mean_hwy = mean(hwy))

ggplot(mpg_year, aes(x = year, y = mean_hwy)) +
  geom_line(color = "steelblue", linewidth = 1) +
  geom_point(color = "steelblue", size = 3) +
  scale_x_continuous(breaks = c(1999, 2008)) +
  labs(
    title = "연도별 평균 고속도로 연비",
    x     = "연도",
    y     = "평균 고속도로 연비 (mpg)"
  ) +
  theme_minimal()

산점도 — geom_point

산점도는 두 연속형 변수의 관계를 볼 때 씁니다. 상관관계, 클러스터, 이상값을 눈으로 확인할 수 있습니다.

기본 산점도와 추세선

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(alpha = 0.5, color = "steelblue") +
  geom_smooth(method = "lm", color = "tomato", se = FALSE) +
  labs(
    title = "배기량과 고속도로 연비",
    x     = "엔진 배기량 (리터)",
    y     = "고속도로 연비 (mpg)"
  ) +
  theme_minimal()

se = FALSE는 신뢰 구간 띠를 숨깁니다. method = "loess"로 바꾸면 곡선 추세선이 그려집니다.

색과 크기로 세 번째 변수 표현

ggplot(mpg, aes(x = displ, y = hwy, color = class, size = cyl)) +
  geom_point(alpha = 0.6) +
  labs(
    title  = "배기량, 연비, 클래스, 실린더 수",
    x      = "엔진 배기량 (리터)",
    y      = "고속도로 연비 (mpg)",
    color  = "클래스",
    size   = "실린더 수"
  ) +
  theme_minimal()

하나의 산점도에서 네 가지 변수(x, y, color, size)를 동시에 표현합니다. 단, 변수가 너무 많으면 오히려 읽기 어렵습니다. 두세 개의 변수가 적당합니다.

텍스트 레이블 추가

특정 데이터 포인트에 이름을 붙이고 싶을 때는 geom_text() 또는 geom_label()을 씁니다.

# 클래스별 평균값 산점도에 레이블 붙이기
mpg_class_avg <- mpg |>
  group_by(class) |>
  summarise(
    mean_displ = mean(displ),
    mean_hwy   = mean(hwy)
  )

ggplot(mpg_class_avg, aes(x = mean_displ, y = mean_hwy, label = class)) +
  geom_point(size = 4, color = "steelblue") +
  geom_text(vjust = -0.8, size = 3.5) +
  labs(
    title = "클래스별 평균 배기량과 평균 연비",
    x     = "평균 배기량 (리터)",
    y     = "평균 고속도로 연비 (mpg)"
  ) +
  theme_minimal()

vjust = -0.8은 레이블을 점 위쪽으로 올립니다. 양수면 아래, 음수면 위로 이동합니다.

어떤 차트를 써야 할까

차트 선택은 "무엇을 말하고 싶은가"에서 출발합니다.

목적 추천 차트 geom
범주별 빈도 비교 막대그래프 geom_bar()
계산된 값 비교 막대그래프 geom_col()
시간에 따른 변화 꺾은선 geom_line()
두 연속형 변수의 관계 산점도 geom_point()
범주 안의 분포 박스플롯/바이올린 geom_boxplot()

가장 흔한 실수는 순서가 없는 범주형 데이터에 꺾은선 그래프를 쓰는 것입니다. 꺾은선은 "이전 값에서 다음 값으로 이어진다"는 의미를 내포합니다. 브랜드별 매출처럼 순서가 없는 데이터에는 막대그래프가 맞습니다.

다음 챕터에서는 데이터의 분포를 시각화하는 방법을 살펴봅니다.