bump chart를 그리고 싶을 때, ggbump package

R아두면 쓸데있는 패키지 이야기 02 ggbump package
R package
ggbump
Author

chichead

Published

February 20, 2022

ggbump package

ggbump’s name

ggplot2는 grammar of graphics(a.k.a. gg)을 토대로 시각화를 만드는 패키지입니다. 2는 ver.2의 의미를 담았죠. gg는 릴랜드 윌킨스의 동명의 책 The Grammar of Graphics에서 따온 건데, 이 책에서 릴랜드는 데이터를 어떻게 시각적으로 표현할 것인지에 대해 다룹니다. gg에 대한 이야기는 나중에 다른 포스트에서 다루도록 하겠습니다.

ggplot2 패키지의 문법 기반 위에서 돌아가는 서브 패키지들은 보통 gg라는 접두사로 시작됩니다. ggbump 역시 ggplot2의 일원이라고 이해할 수 있어요. 그렇다면 bump는 무엇을 의미하는 걸까요? 자동차의 범퍼, 혹은 놀이동산의 범퍼카를 떠올리면 bump의 의미를 유추할 수 있어요. bump는 바로, 충돌을 의미합니다. 충돌과 차트, 어떤 연관이 있는 걸까요?


bump chart

2022 May Bumps, Corpus Christi College

영국의 케임브리지 대학에는 The bump라고 불리는 조정 경기가 있습니다. 케임브리지를 가로지르는 캠 강(river Cam) 은 나란히 경주하기에는 너무 좁아서 한 줄로 경주하는 독특한 조정 경주를 진행해왔어요. 19세기 초부터 시작된 이 경기 이름이 바로 The bump입니다. The bump의 경주 방식은 이렇습니다. 우선 강을 따라 한 줄로 경기를 시작합니다. 각 선수들은 전속력으로 노를 저어 앞에 있는 보트를 따라잡고 충돌(bump)하죠. 그렇게 되면 앞에 있는 조정 팀을 추월한 것으로 인정, 순위가 올라가게 됩니다. 주최 측에서는 경기의 진행 상황을 매핑하는 차트를 그려서 제공했는데, 이 차트를 bump chart라고 불렀습니다. 아래 차트는 2020년 사순절에 치러진 대회(Lent Bump)의 남자부 경기 결과입니다. 어떤 차트인지 감이 오죠?


All about ggbump

geom_sigmoid

library(tidyverse)
library(ggbump)

df <- data.frame(x = 1:6,
                 y = 5:10,
                 xend = 7,
                 yend = -5:0)

head(df)
  x  y xend yend
1 1  5    7   -5
2 2  6    7   -4
3 3  7    7   -3
4 4  8    7   -2
5 5  9    7   -1
6 6 10    7    0

시그모이드 곡선에 필요한 변수는 시작점, 끝점, 그룹 정도입니다. 시작점의 위치는 (x, y) 변수에, 끝점의 위치는 (xend, yend) 변수에 넣으면 되죠. 그리고 어떤 점끼리 이어지는지 그룹을 결정해주면 됩니다. 위의 데이터를 가지고 시그모이드 곡선을 그려보면 총 6개의 선이 그려집니다. (1, 5)와 (7, -5)를 잇는 곡선을 포함해서 말이죠.

library(tidyverse)
library(ggbump)

# geom_sigmoid 함수에서 x, y, xend, yend, group 변수를 지정해주면 됩니다.
# geom_sigmoid 외의 함수는 점(geom_point)과 라벨(geom_text)을 위한 함수입니다.

ggplot(df) +
  geom_sigmoid(aes(x = x, xend = xend, y = y, yend = yend, group = factor(x)), color = "black") +
  geom_point(aes(x = x, y = y)) +
  geom_point(aes(x = xend, y = yend)) +
  geom_text(aes(x = x, y = y, label = paste0("(", x, ", ", y, ")")), vjust = 1.8, size = 3) +
  geom_text(aes(x = xend, y = yend, label = paste0("(", xend, ", ", yend, ")")), 
            vjust = 1.4, size = 3) +
  theme_classic()


geom_bump

bump chart를 그리기 위해선 geom_bump 함수를 사용하면 됩니다. 간단하게 가상의 데이터를 만들어 보겠습니다. 대한민국을 포함해 총 5개 국가(Korea, Japan, China, Russia, India)의 임의 데이터입니다. 아래와 같이 나라명과 연도(2020, 2021, 2022), 그리고 임의의 value값이 포함돼있습니다.

df <- tibble(country = c("Korea", "Korea", "Korea", "Japan", "Japan", "Japan", "China", "China", "China", "Russia", "Russia", "Russia", "India", "India", "India"),
             year = c(2020, 2021, 2022, 2020, 2021, 2022, 2020, 2021, 2022, 2020, 2021, 2022, 2020, 2021, 2022),
             value = c(500, 200, 100, 400, 300, 400, 200, 400, 200, 500, 400, 300, 300, 300, 100))

head(df)
# A tibble: 6 × 3
  country  year value
  <chr>   <dbl> <dbl>
1 Korea    2020   500
2 Korea    2021   200
3 Korea    2022   100
4 Japan    2020   400
5 Japan    2021   300
6 Japan    2022   400

geom_bump 함수를 사용하려면 rank 값이 필요합니다. 각 연도별로 묶어서 value값에 따라 rank 값을 부여하면 되겠습니다. rank 함수를 사용하면 됩니다.

# ties.method는 만일 value값이 동등할경우 어떻게 계산할 것인지 결정하는 부분입니다.
# 보통은 min(동률 순위 중 낮은 값 출력), max(동률 순위 중 높은 값 출력)을 사용합니다.
# 여기선 그냥 겹치지 않게 그리기 위해 random method(순서 상관없이 랜덤)를 선택했습니다.

df <- df |>
  group_by(year) |>
  mutate(rank = rank(value, ties.method = "random")) |>
  ungroup()

head(df)
# A tibble: 6 × 4
  country  year value  rank
  <chr>   <dbl> <dbl> <int>
1 Korea    2020   500     4
2 Korea    2021   200     1
3 Korea    2022   100     2
4 Japan    2020   400     3
5 Japan    2021   300     2
6 Japan    2022   400     5

rank값이 잘 나왔군요. rank값은 값이 크면 클수록 더 높은 숫자가 부여됩니다. 2020년 한국의 value는 500, 일본의 value는 400인데 한국이 4위, 일본이 3위인 걸 보면 알 수 있죠. 우리가 보통 생각하는 순위와는 반대입니다. 위에서 rank를 계산할 때 -value로 계산한다면 이 부분은 해결할 수 있습니다. 여기선 그래프를 그릴 때 y축을 돌려버리는 걸로 처리하겠습니다.

library(wesanderson)

ggplot(df, aes(year, rank, color = country)) +
  geom_bump() +
  theme_classic() +
  theme(legend.position = "none") +
  scale_y_reverse() +
  scale_color_manual(values = wes_palette("Zissou1", n = 5))

부드러운 시그모이드 곡선으로 이뤄진 범프 차트가 만들어졌습니다. scale_color_manual에 들어있는 wes_palette는 이름에서 유추할 수 있듯 웨스 앤더슨 감독의 색감이 담긴 컬러 팔레트입니다. 이 차트에서는 웨스 앤더슨 감독의 2004년 작 <스티브 지소와의 해저 생활(The Life Aquatic With Steve Zissou)>의 색상을 사용했습니다.

The Life Aquatic with Steve Zissou, Wes Anderson

여기서 조금 더 꾸며볼까요? bump line의 폭을 늘리고 점도 찍어보고, 해당 라인이 어떤 국가를 의미하는지 라벨도 달아보겠습니다. 축은 있으면 보기 싫으니 선을 다 없애버립시다. 그리고 x축은 정수 연도만 남겨야 할 것 같고요. 정리해보면 이렇게 될 겁니다.

ggplot(df, aes(year, rank, color = country)) +
  geom_bump(size = 5, smooth = 8, alpha = 0.8) +
  geom_point(size = 5) +
  geom_text(data = df %>% filter(year == min(year)),
            aes(x = year, label = country), size = 5, hjust = 0, vjust = -1) +
  geom_text(data = df %>% filter(year == max(year)),
            aes(x = year, label = country), size = 5, hjust = 1, vjust = -1) +
  theme_minimal() +
  theme(legend.position = "none",
        panel.grid.major = element_blank()) +
  scale_x_continuous(limits = c(2019.95, 2022.05),
                     breaks = seq(2020, 2022, 1)) +
  scale_y_reverse(limits = c(5, 0.5)) +
  labs(y = "RANK",
       x = NULL) +
  scale_color_manual(values = wes_palette("Zissou1", n = 5))