聰明不如鈍筆
총명불여둔필
assignment KindeR

ggplot2 그래프에서 글자색까지 깔맞춤 하기(feat. ggText)

R 조금 더 정확하게는 ggplot2 패키지로 그래프를 그리다 보면 마음에 드는 색깔을 골라서 항목을 칠하고 그 색깔을 계속 유지하고 싶을 때가 있습니다.

 

지난해 5월에 'ggplot2 그래프 색깔 마음대로 바꾸기(feat. scale_*_manual)' 포스트를 썼던 이유입니다.

 

그런데 이 포스트를 통해서도 해결하지 못하는 문제가 하나 남아 있습니다.

 

이 방법으로는 글자 색깔까지 '깔맞춤'으로 유지할 수는 없기 때문입니다.

그래서 세상에 존재하는 패키지가 바로 ggtext입니다.

 

이 패키지는 마크다운HTML 문법을 활용해 ggplot2 그래프에서 글자색을 바꿀 수 있도록 도와줍니다.

 

제일 먼저 할 일은 늘 그렇듯 tidyverse 패키지 (설치하고) 불러오기.

#install.packages('tidyverse')
library('tidyverse')
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.2     v purrr   0.3.4
## v tibble  3.0.3     v dplyr   1.0.2
## v tidyr   1.1.2     v stringr 1.4.0
## v readr   1.3.1     v forcats 0.5.0
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()

 

그리고 물론 ggtext 패키지도 (설치하고) 불러와야 합니다.

#install.packages('ggtext')
library('ggtext')

 

마지막으로 palmerpenguins 패키지도 (설치하고) 불러오겠습니다.

#install.packages('palmerpenguins')
library('palmerpenguins')

 

palmerpenguins 패키지에는 요즘 iris 데이터 대안으로 널리 쓰는 '펭귄' 데이터가 들어 있습니다.

 

펭귄 데이터는 △아델리(Adelie) △젠투(Gentoo) △턱끈(Chinstrap) 펭귄 신체적 특징을 수집한 자료 입니다.

 

그냥 penguins라고 입력하면 어떤 데이터가 들어 있는지 확인하실 수 있습니다.

penguins
## # A tibble: 344 x 8
##    species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
##    <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
##  1 Adelie  Torgersen           39.1          18.7               181        3750
##  2 Adelie  Torgersen           39.5          17.4               186        3800
##  3 Adelie  Torgersen           40.3          18                 195        3250
##  4 Adelie  Torgersen           NA            NA                  NA          NA
##  5 Adelie  Torgersen           36.7          19.3               193        3450
##  6 Adelie  Torgersen           39.3          20.6               190        3650
##  7 Adelie  Torgersen           38.9          17.8               181        3625
##  8 Adelie  Torgersen           39.2          19.6               195        4675
##  9 Adelie  Torgersen           34.1          18.1               193        3475
## 10 Adelie  Torgersen           42            20.2               190        4250
## # ... with 334 more rows, and 2 more variables: sex <fct>, year <int>

 

자, 그럼 이 데이터로 일단 간단한 산점도를 하나 그려보겠습니다.

penguins %>% 
  ggplot(aes(x = species,
             y = body_mass_g,
             color = species)) +
  geom_jitter()
## Warning: Removed 2 rows containing missing values (geom_point).

'ggplot2 그래프 색깔 마음대로 바꾸기(feat. scale_*_manual)' 포스트에서 알아본 것처럼 특정한 색깔을 골라서 점을 찍으려면 이렇게 팔레트를 지정하면 됩니다.

c('Adelie' = '#ff7204',
  'Chinstrap' = '#c85aca',
  'Gentoo' = '#0f7375') -> color_values
penguins %>% 
  ggplot(aes(x = species,
             y = body_mass_g,
             color = species)) +
  geom_jitter() +
  scale_color_manual(values = color_values)
## Warning: Removed 2 rows containing missing values (geom_point).

이제 축 레이블을 색깔을 바꿀 차례. ggtext는 HTML 문법을 활용한다고 했으니까 이렇게 레이블을 지정해 줍니다.

c(
  'Adelie' = "<span style='color: #ff7204'>Adelie</span>",
  'Chinstrap' = "<span style='color: #c85aca'>Chinstrap</span>",
  'Gentoo' = "<span style='color: #0f7375'>Gentoo</span>"
) -> labels

 

이어서 이 자료를 x축 레이블로 지정해 보면…

penguins %>% 
  ggplot(aes(x = species,
             y = body_mass_g,
             color = species)) +
  geom_jitter() +
  scale_color_manual(values = color_values) +
  scale_x_discrete(labels = labels)
## Warning: Removed 2 rows containing missing values (geom_point).

'<span>...</span>'이라고 입력한 내용을 그냥 그대로 출력하고 맙니다.

 

색깔을 지정하라면 theme() 함수 안에 element_markdown() 옵션을 줘야 합니다.

 

우리는 x축 레이블을 바꾸려는 거니까 axis.text.x = element_markdown()이라고 지정하면 됩니다.

penguins %>% 
  ggplot(aes(x = species,
             y = body_mass_g,
             color = species)) +
  geom_jitter() +
  scale_color_manual(values = color_values) +
  scale_x_discrete(labels = labels) +
  theme(axis.text.x = element_markdown())
## Warning: Removed 2 rows containing missing values (geom_point).

물론 그래프 제목(plot.title)에도 element_markdown()을 적용할 수 있습니다.

 

제목에도 펭귄 종류별로 색깔을 달리하고 싶을 때는 그냥 이렇게 쓰면 그만입니다.

penguins %>% 
  ggplot(aes(x = species,
             y = body_mass_g,
             color = species)) +
  geom_jitter() +
  scale_color_manual(values = color_values) +
  scale_x_discrete(labels = labels) +
  labs(title = "<span style='color: #ff7204'>Adelie</span>, <span style='color: #c85aca'>Chinstrap</span>, <span style='color: #0f7375'>Gentoo</span> 몸무게 분포") +
  theme(plot.title = element_markdown(),
        axis.text.x = element_markdown())
## Warning: Removed 2 rows containing missing values (geom_point).

혹시 오른쪽 범례에 있는 글자 색깔도 똑같이 맞추고 싶지 않으신가요?

 

이때는 그냥 scale_x_discrete() 함수 안에 똑같이 레이블을 지정하면 그만입니다.

penguins %>% 
  ggplot(aes(x = species,
             y = body_mass_g,
             color = species)) +
  geom_jitter() +
  scale_color_manual(values = color_values,
                     labels = labels) +
  scale_x_discrete(labels = labels) +
  labs(title = "<span style='color: #ff7204'>Adelie</span>, <span style='color: #c85aca'>Chinstrap</span>, <span style='color: #0f7375'>Gentoo</span> 몸무게 분포") +
  theme(plot.title = element_markdown(),
        axis.text.x = element_markdown(),
        legend.text = element_markdown())
## Warning: Removed 2 rows containing missing values (geom_point).

이 블로그를 꾸준히 읽어 오신 분은 이 정도에서 아쉬운 생각이 드실지도 모릅니다.

 

스포츠 데이터를 활용하는 게 이 블로그 전통이니까요.

 

그래서 이번에는 지난해(2020년) 프로야구 타율 상위 20위 데이터를 가지고 연습을 한 번 더 해보겠습니다.

 

먼저 avg라는 변수에 데이터를 입력합니다.

tribble(
    ~player, ~team,  ~avg,  ~code,
    "최형우", "KIA", 0.354, 72443,
    "손아섭",  "롯데", 0.352, 77532,
    "로하스",  "KT", 0.349, 67025,
    "박민우",  "NC", 0.345, 62907,
  "페르난데스",  "두산",  0.34, 69209,
    "이정후",  "키움", 0.333, 67341,
    "허경민",  "두산", 0.332, 79240,
    "김현수",  "LG", 0.331, 76290,
    "강백호",  "KT",  0.33, 68050,
    "양의지",  "NC", 0.328, 76232,
    "나성범",  "NC", 0.324, 62947,
    "황재균",  "KT", 0.312, 76313,
    "김동엽",  "삼성", 0.312, 66838,
    "오재일",  "두산", 0.312, 75334,
    "구자욱",  "삼성", 0.307, 62404,
    "최주환",  "두산", 0.306, 76267,
     "터커", "KIA", 0.306, 69652,
    "박석민",  "NC", 0.306, 74465,
    "이명기",  "NC", 0.306, 76849,
    "김하성",  "키움", 0.306, 64300
  ) -> avg

 

이번에는 데이터 프레임(조금 더 정확하게는 tibble) 형태로 팔레트를 지정합니다.

tribble(
  ~team, ~color,
  '두산', '#131230',
  '롯데', '#002955',
  '삼성', '#074ca1',
  '키움', '#7c0022',
  '한화', '#ff6600',
  'KIA', '#c70125',
  'KT', '#000000',
  'LG', '#c30452',
  'NC', '#315288',
  'SK', '#ff0000') -> kbo_palette

 

일단 그래프 색깔만 칠해보겠습니다. 아직은 데이터가 따로 따로 있으니까 left_join() 함수로 붙여봅니다.

avg %>% 
  left_join(kbo_palette)
## Joining, by = "team"
## # A tibble: 20 x 5
##    player     team    avg  code color  
##    <chr>      <chr> <dbl> <dbl> <chr>  
##  1 최형우     KIA   0.354 72443 #c70125
##  2 손아섭     롯데  0.352 77532 #002955
##  3 로하스     KT    0.349 67025 #000000
##  4 박민우     NC    0.345 62907 #315288
##  5 페르난데스 두산  0.34  69209 #131230
##  6 이정후     키움  0.333 67341 #7c0022
##  7 허경민     두산  0.332 79240 #131230
##  8 김현수     LG    0.331 76290 #c30452
##  9 강백호     KT    0.33  68050 #000000
## 10 양의지     NC    0.328 76232 #315288
## 11 나성범     NC    0.324 62947 #315288
## 12 황재균     KT    0.312 76313 #000000
## 13 김동엽     삼성  0.312 66838 #074ca1
## 14 오재일     두산  0.312 75334 #131230
## 15 구자욱     삼성  0.307 62404 #074ca1
## 16 최주환     두산  0.306 76267 #131230
## 17 터커       KIA   0.306 69652 #c70125
## 18 박석민     NC    0.306 74465 #315288
## 19 이명기     NC    0.306 76849 #315288
## 20 김하성     키움  0.306 64300 #7c0022

이 코드가 이해가 가지 않으시는 분은 'R에서 엑셀 vlookup() 함수 쓰기(feat. *_join())' 포스트가 도움이 될 수 있습니다.

 

색상 코드 그 자체가 들어 있으니까 scale_color_identity() 함수를 쓰면 팔레트에 따라 색을 칠할 수 있습니다.

avg %>% 
  left_join(kbo_palette) %>% 
  ggplot(aes(x = player %>% fct_reorder(avg),
             y = avg,
             fill = color)) +
  geom_col() +
  coord_flip() +
  scale_fill_identity()
## Joining, by = "team"

여기서 글씨 색을 맞추는 첫 번째 방법은 앞에서 했던 것처럼 선수 20명 코드를 하나 하나 쓰는 것.

 

이러면 너무 귀찮겠죠? 그래서 세상에는 glue 패키지가 우리를 기다리고 있습니다.

#install.packages('glue')
library('glue')
## 
## Attaching package: 'glue'
## The following object is masked from 'package:dplyr':
## 
##     collapse

 

풀이라는 뜻 그대로 glue는 텍스트 일부분을 다른 낱말(?)로 바꿔주는 구실을 합니다.

 

예를 들어 '내 이름은 ○○입니다'에 이름만 바꿔 넣는 코드는 이렇게 쓸 수 있습니다.

c('영희', '철수') -> names
glue('내 이름은 {names}입니다.')
## 내 이름은 영희입니다.
## 내 이름은 철수입니다.

 

레이블도 똑같은 구조에서 색깔이랑 이름만 바꾸면 그만이니까 이렇게 쓰면 원하는 결과를 얻을 수 있습니다.

avg %>% 
  left_join(kbo_palette) %>% 
  mutate(labels = glue("<span style='color:{color}'>{player}</span>"))
## Joining, by = "team"
## # A tibble: 20 x 6
##    player     team    avg  code color   labels                                  
##    <chr>      <chr> <dbl> <dbl> <chr>   <glue>                                  
##  1 최형우     KIA   0.354 72443 #c70125 <span style='color:#c70125'>최형우</spa~
##  2 손아섭     롯데  0.352 77532 #002955 <span style='color:#002955'>손아섭</spa~
##  3 로하스     KT    0.349 67025 #000000 <span style='color:#000000'>로하스</spa~
##  4 박민우     NC    0.345 62907 #315288 <span style='color:#315288'>박민우</spa~
##  5 페르난데스 두산  0.34  69209 #131230 <span style='color:#131230'>페르난데스<~
##  6 이정후     키움  0.333 67341 #7c0022 <span style='color:#7c0022'>이정후</spa~
##  7 허경민     두산  0.332 79240 #131230 <span style='color:#131230'>허경민</spa~
##  8 김현수     LG    0.331 76290 #c30452 <span style='color:#c30452'>김현수</spa~
##  9 강백호     KT    0.33  68050 #000000 <span style='color:#000000'>강백호</spa~
## 10 양의지     NC    0.328 76232 #315288 <span style='color:#315288'>양의지</spa~
## 11 나성범     NC    0.324 62947 #315288 <span style='color:#315288'>나성범</spa~
## 12 황재균     KT    0.312 76313 #000000 <span style='color:#000000'>황재균</spa~
## 13 김동엽     삼성  0.312 66838 #074ca1 <span style='color:#074ca1'>김동엽</spa~
## 14 오재일     두산  0.312 75334 #131230 <span style='color:#131230'>오재일</spa~
## 15 구자욱     삼성  0.307 62404 #074ca1 <span style='color:#074ca1'>구자욱</spa~
## 16 최주환     두산  0.306 76267 #131230 <span style='color:#131230'>최주환</spa~
## 17 터커       KIA   0.306 69652 #c70125 <span style='color:#c70125'>터커</span> 
## 18 박석민     NC    0.306 74465 #315288 <span style='color:#315288'>박석민</spa~
## 19 이명기     NC    0.306 76849 #315288 <span style='color:#315288'>이명기</spa~
## 20 김하성     키움  0.306 64300 #7c0022 <span style='color:#7c0022'>김하성</spa~

 

이제 어려울 게 없습니다. 그냥 앞에서처럼 쓰면 됩니다.

avg %>% 
  left_join(kbo_palette) %>% 
  mutate(labels = glue("<span style='color:{color}'>{player}</span>")) %>% 
  ggplot(aes(x = labels %>% fct_reorder(avg),
             y = avg,
             fill = color,
             color = color)) +
  geom_col() +
  coord_flip() +
  scale_fill_identity() +
  scale_color_identity() +
  theme(axis.text.y = element_markdown())
## Joining, by = "team"

자세히 보시면 avg 변수에 '코드'라는 열이 있다는 걸 알 수 있습니다. 한국야구위원회(KBO)에서 선수 구분에 쓰는 숫자입니다.

 

이 숫자를 활용하면 프로필 사진을 찾아서 이런 그래프도 한번에 그릴 수 있습니다.

avg %>% 
  left_join(kbo_palette) %>% 
  mutate(labels = glue("<span style='color: {color};'>{player} </span><img src='https://lgcxydabfbch3774324.cdn.ntruss.com/KBO_IMAGE/person/middle/2020/{code}.jpg' width='20'>")) %>% 
  ggplot(aes(x = labels %>% fct_reorder(avg),
             y = avg,
             fill = color,
             color = color)) +
  geom_col() +
  coord_flip() +
  scale_fill_identity() +
  scale_color_identity() +
  theme(axis.text.y = element_markdown())
## Joining, by = "team"

이 정도면 ggtext 활용법을 알아두셔도 나쁠 게 없다는 생각이 들지 않으시나요?

 

그럼 모두들 Happy Charting -_-)/

 

댓글,

KindeR | 카테고리 다른 글 더 보기