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 -_-)/
댓글,