본문 바로가기
  • plotly로 바로쓰는 동적시각화 in R & 파이썬
ggplot2

벗꽃피는 순서대로 망한다? 지역별 대학생수 증감 - dumbbell 차트

by 아참형인간 2021. 8. 19.
dumbbell.knit

덤벨(Dumbbell) 차트는 동일한 변수의 두개의 값을 비교하기 위해 사용하는 차트이다. 일반적으로 양쪽 끝을 둥글게 만들고 그 사이를 선으로 연결하여 생긴 형태가 운동할때 쓰는 아령과 같이 생겨서 붙여진 이름이다.

ggplot2를 사용하여 덤벨 차트를 만들때는

  1. ggalt 패키지의 geom_dumbbell()을 이용하는 방법
  2. geom_line()을 사용하는 방법
  3. geom_segment()를 사용하는 방법의 세가지 방법을 사용할 수 있다.

이번 포스트에서는 이 세가지 방법을 모두 사용하여 덤벨 차트를 만들어 본다.

최근 저출산 고령화 사회로 진입함에 따라 점차 신생아수가 지속적으로 줄어들었다. 이러한 신생아수 감소가 2020년부터 본격적으로 대학 학생수에 영향을 미치기 시작했는데 이러한 현상으로 ’벗꽂 피는 순서대로 대학이 망한다’는 이야기가 나오고 있다.

덤벨 차트를 사용하여 2019년과 2020년의 각 지역별 대학 학생수를 비교해보도록 하겠다.

실습에 사용하는 데이터는 교육통계 서비스 홈페이지 https://kess.kedi.re.kr에서 제공하는 대학 시도별 학생수를 사용하였다.

데이터 로딩 및 전처리

대학 시도별 학생수에서 다운받은 파일(대학 시도별 학생수.xlsx)의 데이터를 로딩하면 다음과 같다.

이 데이터로 차트를 그리면 각 시도간의 학생수의 차이가 커서 차트의 효율성이 많이 떨어진다. 그래서 2019년 학생수를 100으로 두고 2020년 학생수의 비율로 표현하겠다. 또 이 학생수의 비율이 100보다 크다면 증가를, 100보다 작으면 감소를 표현하는 열을 추가로 만들었다.

library(readxl)
library(tidyverse)

#  엑셀 파일을 읽어 df 데이터프레임에 로딩
df <- read_excel('./대학 시도별 학생수.xlsx', na = '-', sheet = 'Sheet0', col_types = c(rep('numeric', 19)), col_names = T)

# df 중에서 필요없는 행을 삭제
df <- df[-(42:49),]

# 덤벨 차트 생성에 필요한 rate.df를 만든다. 
rate.df <- df |> 
  ## 연도가 2019년 이상만 필터링
  filter(연도 >= 2019) |>       
  ## 2번열부터 19번 열까지를 긴형태의 데이터 프레임으로 변환
  gather(key = '지역', value = '학생수', 2:19) |>
  ## 긴형태로 변환된 데이터프레임을 다시 2019열과 2020열을 만들어 넓은 형태로 변환
  spread(key = 연도, value = 학생수) |>
  ## 2019년 대비 2020년 학생수 비율이 저장된 열과 증가/감소가 표현되는 열을 생성
  mutate(rate = (`2020`/`2019`)*100, rate1 = 100, pos = ifelse(rate >= 100, '증가', '감소')) |>
  ## 차트 생성에 필요한 열만 선택
  select(1, 4, 5, 6)

## 열 이름을 변경
colnames(rate.df) <- c('지역', '2020', '2019', 'pos')

## 지역 열을 factor로 변환하면서 level을 정의
rate.df$지역 <- fct_relevel(rate.df$지역, '합계', '서울', '부산', '대구', '인천', '광주', '대전', '울산', '세종', '경기', '강원', '충북', '충남', '전북', '전남', '경북', '경남', '제주')

ggalt 패키지의 geom_dumbbell()

ggalt패키지는 ggplot2 패키지를 기반으로 특별한 좌표 시스템, 도형(geom), 통계적 변환, 스케일, 폰트등을 제공하는 패키지이다.

이 중 geom_dumbbell()을 사용하여 덤벨 차트를 생성하는 코드는 다음과 같다.

덤벨 차트를 그릴때 다른 차트와 다른점은 xend 매개변수를 설정해야 한다는 것이다. 덤벨 차트는 x부터 xend까지의 선을 그리는 차트이다.

library(ggalt)

rate.df |>
  ggplot(aes(x = `2019`, xend = `2020`, y = 지역, group = 지역)) +
  geom_dumbbell() 

geom_dumbbell()은 다음과 같이 몇가지 추가적인 유용한 옵션이 있다.

  • colour_x : 시작점의 색깔 설정
  • size_x : 시작점의 크기 설정
  • colour_xend : 마지막 점의 색깔 설정
  • size_xend : 마지막 점의 크기 설정
  • dot_guide : 가이드 라인 설정
rate.df |>
  ggplot(aes(x = `2019`, xend = `2020`, y = 지역, group = 지역)) +
  geom_dumbbell(aes(colour = as.factor(pos), colour_x = pos), colour_xend = 'black', size = 2.0) 

차트가 좀 투박해서 ggplot2의 기능을 이용해 차트를 다음과 같이 꾸며보았다.

rate.df |> 
  ggplot(aes(x = `2020`, xend = `2019`, y = 지역, group = 지역)) +
  geom_dumbbell(aes(colour = as.factor(pos), colour_x = pos), colour_xend = 'black', size = 2.0) + 
  geom_text(aes(x = `2020`, label = ifelse(pos == '감소', paste0(round(`2020`-100, 1), '%'), NA)), vjust = 1.5) +
  geom_text(aes(x = `2020`, label = ifelse(pos == '증가', paste0(round(`2020`-100, 1), '%'), NA)), vjust = -1) + xlim(95, 100.5) + 
  coord_flip() +
  scale_color_manual(values = c('red', 'blue')) + labs(title = '2019년 대비 2020년 지역별 고등교육기관 학생수 비율', x = '비율(%)', color = '증감')

ggplot2 패키지의 geom_line()

geom_line()으로 덤벨 차트를 그릴때는 geom_dumbbell()과는 달리 xend를 설정하지 않는다. 따라서 동일한 라인으로 표현되는 두 개의 값이 같은 열에 저장되어야 한다. 그래서 앞서 생성했던 rate.df의 2019열과 2020열을 다시 하나의 열로 만든 후에 그릴 수 있다.

rate.df |>
  gather(rate, value, 2, 3) |>
  ggplot(aes(x = value, y = 지역)) +
  geom_line(aes(group = 지역, colour = as.factor(pos)), size = 2) + 
  geom_point(aes(colour = as.factor(pos)), size = 3) +
  geom_text(aes(x = value, label = ifelse((rate == '2020' & pos == '감소'), paste0(round(value-100, 1), '%'), NA)), vjust = 1.5) +
  geom_text(aes(x = value, label = ifelse((rate == '2020' & pos == '증가'), paste0(round(value-100, 1), '%'), NA)), vjust = -1) +
  xlim(95, 100.5) + 
  theme(legend.position="top") +
  coord_flip() + 
  labs(title = '2019년 대비 2020년 지역별 고등교육기관 학생수 비율', x = '비율(%)', color = '증감')

ggplot2 패키지의 geom_segment()

geom_segment()로 덤벨 차트를 그리는 것은 ggaltgeom_dumbbell()을 사용하는 방법과 유사하게 xend를 설정함으로써 그릴 수 있다. 여기서는 화살표를 추가할수 있다는 점이 다르다.

rate.df |>
  ggplot(aes(x = `2019`, xend = `2020`, y = 지역, yend = 지역)) +
  geom_segment(aes(group = 지역, colour = as.factor(pos)), size = 2, arrow = arrow(length = unit(0.03, "npc"))) + 
  geom_point(aes(colour = as.factor(pos))) +
  geom_text(aes(x = `2020`, label = ifelse(pos == '감소', paste0(round(`2020`-100, 1), '%'), NA)), vjust = 1.5) +
  geom_text(aes(x = `2020`, label = ifelse(pos == '증가', paste0(round(`2020`-100, 1), '%'), NA)), vjust = -1) + xlim(95, 100.5) + 
  theme(legend.position="bottom") +
  xlim(95, 100.5) +
  coord_flip() + 
  labs(title = '2019년 대비 2020년 지역별 고등교육기관 학생수 비율', x = '비율(%)', color = '증감')

댓글