R 시각화 1

ggplot2 패키지를 활용한 시각화 1

Dr.Kevin 4/2/2018

우리는 이전 포스팅에서 탐색적 데이터 분석에 대해 알아봤습니다. R 기본 함수들을 이용하여 몇 가지 그래프를 그려보았는데요. 이번 포스팅에서는 R 시각화의 대표 패키지인 ggplot2를 활용하여 앞서 EDA에서 그려보았던 그래프들을 재현(?)해보겠습니다. 어떤 그래프가 더 나은지에 대한 가치판단은 각자의 몫으로 남기겠습니다만, 저는 ggplot2 패키지가 조금 더 세련된 것 같습니다. ㅎㅎ

ggplot2 패키지 소개

우리가 R 기본함수로 그래프를 그릴 때 사용했던 함수들 기억하시나요? plot, hist, boxplot, barplot 등으로 그래프를 그리고 text, abline, legend 등의 함수들을 추가했습니다. 그런데 ggplot2 패키지는 조금 다른 문법을 사용합니다.

아래 그림은 ggplot2 패키지로 그래프를 그릴 때 여러 개의 층으로 나누어 표현한다는 것을 알 수 있습니다.[1]

그림은 총 7개의 층으로 구성되어 있으며, 맨 아래 Data로부터 Geometries는 필수로 입력해야 하는 항목들이고, Facets부터 맨 위에 있는 Theme까지는 필요한 경우 추가하면 되는 항목들입니다.

이번에는 R 코드로 문법을 설명하는 다른 그림을 소개하겠습니다.[2]

RStudio 홈페이지에는 주요 기능들에 대해서 압축하여 설명해놓은 여러 가지 Cheat Sheets을 PDF로 다운로드할 수 있습니다. 이번 포스팅과 관련 있는 Data Visualization Cheat Sheet를 다운로드한 후 열어보면 ggplot2 패키지의 주요 사용법이 소개되어 있는데요. 두 번째로 소개하는 그림은 Cheat Sheet 왼쪽에 있는 이미지의 일부를 개인 블로거가 올려놓은 것입니다.

첫 번째 그림과는 순서가 바뀌었지만, R 코드를 입력하는 순서로는 두 번째 그림이 맞습니다. 먼저 Data 부분은 ggplot() 함수로 지정해주고, Geometry 부분은 geom_function() 함수로 지정해줍니다. geom_function()은 여러 가지가 있는데요. 산점도를 그릴 때에는 geom_point(), 선그래프는 geom_line(), 막대그래프는 geom_bar() 등을 사용하는 것입니다. 필수 항목 중 Aestheticggplot() 함수 또는 geom_function() 함수의 mapping 인자에 aes()로 할당하는 방식으로 그래프에 적용할 수 있는데요. 전체 그림에 적용하려면 ggplot() 함수에 할당하고, 특정 그래프에 적용하려면 해당 geom_function() 함수에 할당하면 됩니다.

기본 함수 소개

  • ggplot(data, mapping = aes(), …) + geom_function(data, mapping = aes(), stat, position, …)

  • geom_function()의 종류

    • geom_point() : 산점도
    • geom_line() : 선그래프
    • geom_bar() : 막대그래프
    • geom_col() : 막대그래프
    • geom_histogram() : 히스토그램
    • geom_boxplot() : 상자수염그림
    • geom_text() : 문자 삽입
    • geom_abline() : 선형회귀식 삽입
    • geom_vline() : 수직선 삽입
    • geom_hline() : 수평선 삽입
  • 추가 옵션 : 자세한 사항은 Cheat Sheet를 참조하세요.

    • stats : 새로운 데이터 생성
    • scale : 범위 조정
    • coordinate : x, y축 조정
    • position : geom 정렬 방식 조정
    • theme : 테마 적용
    • facet : x, y축 분할
    • label : 축이름, 주석 등 설정
    • legend : 범례 설정
    • zoom : 축 범위 지정 등

R 기본함수와 ggplot 패키지 함수로 그린 산점도 비교

지금까지 설명으로 뭐가 뭔지 이해하기 어려웠을 겁니다. 기본 예제를 하나 실행하면서 어렴풋이나마 ggplot의 기능을 확인해보겠습니다. 이번 포스팅에서도 프로야구 데이터를 사용합니다. 데이터를 읽고 시각화에 필요한 처리를 한 후, R 기본함수와 ggplot2 패키지 함수를 이용해서 산점도를 그려보겠습니다.

# 필요 패키지를 불러옵니다. 
library(readxl)

# xlsx 파일을 읽어, dataXls에 할당합니다. 
hitters <- read_excel(path = './data/2017_Baseball_hitter_stat.xlsx', sheet = NULL)

# 불필요한 열(순위)을 삭제합니다.
hitters <- hitters[, -1]

# 팀명을 범주형 벡터로 변환합니다. 
hitters$팀명 <- as.factor(hitters$팀명)

# 관심선수 명단을 벡터에 할당합니다. 
관심선수 <- c('이명기', '김주찬', '버나디나', '최형우', '나지완', '안치홍', '이범호', '김민식', '김선빈')

# 관심선수만 추출하여 hittersFP 객체에 할당합니다. 
hittersFP <- hitters[hitters$선수명 %in% 관심선수 & hitters$팀명 == 'KIA', ]

# 50 타수 이상인 타자만 추출하여 hitters50 객체에 할당합니다. 
hitters50 <- hitters[hitters$타수 >= 50, ]

R 기본함수로 산점도 그리기

# 타수와 타율을 기준으로 산점도를 그려봅니다. 
plot(x = hitters50$타수,
     y = hitters50$타율,
     main = '타수와 타율 간 관계 (50타수 이상 타자)',
     xlab = '타수',
     ylab = '타율',
     family = 'NanumGothic')

# 타수와 타율 간 선형관계를 확인하고자 단순선형회귀선을 추가합니다.
abline(reg = lm(formula = 타율 ~ 타수, data = hitters50),
       col = 'red',
       lty = 2)

# 관심선수를 산점도 위에 빨간 점으로 추가합니다. 
points(x = hittersFP$타수,
       y = hittersFP$타율,
       col = 'red',
       pch = 16,
       lwd = 2)

# 빨간 점 왼쪽에 선수 이름을 출력합니다.
text(x = hittersFP$타수,
     y = hittersFP$타율,
     labels = hittersFP$선수명,
     col = 'blue',
     pos = 2,
     cex = 0.8,
     font = 2,
     family = 'NanumGothic')

ggplot2로 산점도 그리기

이번에는 ggplot2 패키지 함수로 앞에서 그린 산점도를 재현해보겠습니다. 그리고 나서 각각의 함수 기능에 대해 설명을 하도록 하겠습니다.

# 필요 패키지를 불러옵니다. 
library(ggplot2)

# 산점도를 그립니다. 
# 데이터와 전체 데이터에 적용할 에스테틱을 설정합니다. 
ggplot(data = hitters50,
       mapping = aes(x = 타수,
                     y = 타율)) +
  
  # 산점도를 그립니다. 
  geom_point(shape = 1) + 
  
  # 회귀선을 추가합니다. 
  stat_smooth(method = 'lm',
              lty = 2,
              color = 'red',
              se = FALSE) +
  
  # 관심선수 산점도를 추가합니다. 에스테틱을 별도로 설정합니다. 
  geom_point(data = hittersFP,
             mapping = aes(x = 타수,
                           y = 타율),
             shape = 16,
             size = 3,
             color = 'red') +
  
  # 관심선수의 이름을 출력합니다. 
  geom_text(data = hittersFP,
            mapping = aes(x = 타수,
                          y = 타율,
                          label = 선수명),
            color = 'blue',
            hjust = 1.2,
            size = 3,
            fontface = 'bold',
            family = 'NanumGothic') +
  
  # 그래프 제목과 x, y축명을 설정합니다. 
  ggtitle(label = '타수와 타율 간 관계 (50타수 이상 타자)') + 
  labs(x = '타수',
       y = '타율') +
  
  # Black & White 테마를 적용합니다. 
  theme_bw()

두 가지 그래프 중 어떤 것이 마음에 드시나요? 그리고 어떤 방식이 더 어렵게 느껴졌나요? 제 생각에 ggplot2 방식이 조금 더 어려웠을 것 같습니다. 처음이라 익숙하지 않아서 그런 것이라고 생각합시다! 이제부터 자주 사용하여 익숙해지면 한결 나아질 겁니다.

geom_point() 함수의 주요 인자

산점도를 그릴 때 geom_point() 함수에서 사용되는 주요 인자들을 하나씩 설명해드리겠습니다. 산점도 뿐만 아니라 다른 그래프에도 공통으로 적용되는 부분이니 잘 익혀두기 바랍니다.

  • mapping : aes() 함수에 아래 인자들을 할당하면 데이터에 따라 해당 인자가 적용되도록 설정할 수 있습니다. 반대로 aes() 밖에서 지정하면 모든 점에 대해서 같은 설정이 적용됩니다.
  • color : 점의 테두리 색상을 지정합니다.
  • fill : 점의 채우기 색상을 지정합니다.
  • size : 점의 크기를 지정합니다.
  • shape : 점의 모양을 지정합니다.
  • stroke : 점의 테두리 두께를 지정합니다.
  • alpha : 점의 투명도를 지정합니다. 0 ~ 1 값을 가지며, 숫자가 작을수록 투명합니다.

점의 모양은 아래 그림을 보고 원하는 모양을 선택하여 정수형으로 할당하면 됩니다. 0 ~ 20번까지는 채우기 색상 없이 테두리 색상만 지정할 수 있고, 21 ~ 25번은 테두리와 채우기 색상을 선택할 수 있습니다.

위에서 설명한 인자들을 하나씩 변경해가면서 어떻게 동작하는지 확인해보겠습니다.

# 산점도에 사용될 데이터와 전체 에스테틱을 ggpoint에 할당합니다. 
ggpoint <- ggplot(data = hitters50,
                  mapping = aes(x = 장타율,
                                y = 출루율))

# 산점도를 그립니다. 
ggpoint + geom_point()

점의 모양 변경하기 (shape 인자 사용)

# 모든 점의 모양을 변경합니다.
ggpoint + geom_point(shape = 22)

점의 테두리 색 변경하기 (color 인자 사용)

# 모든 점의 테두리 색을 변경합니다.
ggpoint + 
  geom_point(shape = 22,
             color = 'red')

2018년 4월 2일 현재 R에서 사용할 수 있는 색상명은 총 657개입니다. RStudio 콘솔창에서 colors() 함수를 실행해보면 색상명을 벡터러 출력합니다. 색상명에 대응하는 색상을 확인하고 싶은 분은 Colors in R PDF 파일을 확인하기 바랍니다.

점의 채우기 색 변경하기 (fill 인자 사용)

# 모든 점의 채우기 색을 변경합니다.
ggpoint + 
  geom_point(shape = 22,
             color = 'red',
             fill = 'gray30')

점의 크기 색 변경하기 (size 인자 사용)

# 모든 점의 크기를 변경합니다.
ggpoint + 
  geom_point(shape = 22,
             color = 'red',
             fill = 'gray30',
             size = 3)

점의 테두리 두께 변경하기 (stroke 인자 사용)

# 모든 점의 테두리 두께를 변경합니다.
ggpoint + 
  geom_point(shape = 22,
             color = 'red',
             fill = 'gray30',
             size = 3,
             stroke = 1)

점의 투명도 변경하기 (alpha 인자 사용)

alpha가 0에 가까울수록 투명하고, 반대로 1에 가까울수룩 불투명해집니다.

# 모든 점의 투명도를 변경합니다.
ggpoint + 
  geom_point(shape = 22,
             color = 'red',
             fill = 'gray30',
             size = 3,
             stroke = 1,
             alpha = 0.3)

특정 데이터를 기준으로 주요 인자 적용하기

지금까지 6개 주요 인자들에 대해서 어떻게 작동하는지 하나씩 확인하였습니다. 아직은 모든 점에 대해서 공통적으로 적용되었는데요. mapping = aes() 안에 인자를 할당하면 특정 데이터에 따라 다르게 적용되도록 할 수 있습니다.

점의 모양 변경하기 (aes()에 shape 인자 적용)

점의 모양을 다르게 적용할 데이터로는 문자형 또는 범주형 변수를 할당하며, 레벨이 6개 이상인 경우 6종류만 서로 다르게 출력되고 그 이상은 산점도에 출력되지 않으니 주의하기 바랍니다.

# 팀명을 기준으로 점의 모양을 다르게 합니다.
ggpoint + geom_point(mapping = aes(shape = 팀명))
## Warning: The shape palette can deal with a maximum of 6 discrete values
## because more than 6 becomes difficult to discriminate; you have
## 10. Consider specifying shapes manually if you must have them.

## Warning: Removed 67 rows containing missing values (geom_point).

그래프가 복잡하고 이상합니다. 실습을 위해 프로야구 10개 구단을 올스타팀으로 나눠보겠습니다.

# 올스타팀 컬럼을 새로 만듭니다. 
hitters50$올스타팀 <- ifelse(test = hitters50$팀명 %in% c('NC', '넥센', 'LG', 'KIA', '한화'),
                           yes = '나눔',
                           no = '드림')

# 데이터가 변경되었으므로 ggpoint 객체를 다시 지정합니다. 
ggpoint <- ggplot(data = hitters50,
                  mapping = aes(x = 장타율,
                                y = 출루율))

# 올스타팀을 기준으로 점의 모양을 다르게 합니다. 
ggpoint + geom_point(mapping = aes(shape = 올스타팀))

올스타팀을 기준으로 점의 모양을 다르게 해보니 팀명을 기준으로 하는 것보다는 한결 나아졌습니다만, 한 눈에 차이가 보이지는 않습니다. 올스타팀을 기준으로 점의 모양과 테두리 색을 다르게 출력되도록 하겠습니다.

점의 테두리 색 변경하기 (aes()에 color 인자 적용)

# 올스타팀을 기준으로 점의 테두리 색을 다르게 합니다.
ggpoint + 
  geom_point(mapping = aes(shape = 올스타팀,
                           color = 올스타팀))

이번에는 숫자형 데이터인 OPS를 기준으로 색을 다르게 해보겠습니다. color에 연속형 데이터가 할당되면 그라데이션으로 표현합니다.

# OPS를 기준으로 점의 테두리 색을 다르게 합니다.
ggpoint + 
  geom_point(mapping = aes(shape = 올스타팀,
                           color = OPS))

OPS가 낮을수록 짙은 색으로 출력되고, 반대로 OPS가 높을수록 옅은 색으로 출력되었습니다. 기본 색이 마음에 들지 않으면 scale_color_gradient() 함수를 사용하여 분석가가 임의의 색을 지정할 수 있습니다.

# 분석가가 그라데이션 색을 임의로 지정할 수 있습니다. 
ggpoint + 
  geom_point(mapping = aes(shape = 올스타팀,
                           color = OPS)) + 
  scale_color_gradient(low = 'gray90',
                       high = 'gray10')

점의 채우기 색 변경하기 (aes()에 fill 인자 적용)

채우기 색을 적용하기 위해 shapeaes() 바깥으로 뺀 것과 scale_fill_gradient() 함수를 사용한 것에 주목하기 바랍니다.

# OPS를 기준으로 점의 채우기 색을 다르게 합니다.
ggpoint + 
  geom_point(mapping = aes(fill = OPS),
             shape = 21) +
  scale_fill_gradient(low = 'yellow',
                      high = 'red')

점의 크기 변경하기 (aes()에 size 인자 적용)

홈런 개수만큼 점의 크기를 다르게 하면 홈런 잘치는 타자들이 어떻게 분포하는지 쉽게 확인할 수 있습니다.

# 홈런 개수를 기준으로 점의 크기를 다르게 합니다.
ggpoint + 
  geom_point(mapping = aes(shape = 올스타팀,
                           color = 올스타팀,
                           size = 홈런)) 

그래프 꾸미기

마지막으로 그린 산점도에 수평선과 수직선을 추가하고, 제목과 입력한 후 깔끔한 테마를 적용하여 마무리해보도록 하겠습니다. 수평선은 y축인 출루율의 평균을 기준으로 그리고, 수평선은 x축인 장타율의 평균으로 그립니다.

# 다양한 테마를 사용하기 위해 관련 패키지를 불러옵니다. 
library(ggthemes)

# 수평선과 수직선을 추가하고, 제목을 입력한 후 깔끔한 테마를 적용합니다. 
ggpoint + 
  geom_point(mapping = aes(shape = 올스타팀,
                           color = 올스타팀,
                           size = 홈런)) +
  geom_hline(yintercept = mean(hitters50$출루율),
             color = 'red',
             linetype = 2,
             size = 0.5) +
  geom_vline(xintercept = mean(hitters50$장타율),
             color = 'red',
             linetype = 2,
             size = 0.5) +
  ggtitle(label = '2017 프로야구 타자 스탯 비교') + 
  labs(x = '장타율(Slugging Average',
       y = '출루율(On-Base Percentage)') +
  theme_gdocs()

테마(theme) 정리

  • ggplot2 패키지 테마

    • theme_gray() : The signature ggplot2 theme with a grey background and white gridlines, designed to put the data forward yet make comparisons easy.
    • theme_bw() : The classic dark-on-light ggplot2 theme. May work better for presentations displayed with a projector.
    • theme_linedraw() : A theme with only black lines of various widths on white backgrounds, reminiscent of a line drawings.
    • theme_light() : A theme similar to theme_linedraw but with light grey lines and axes, to direct more attention towards the data.
    • theme_dark() : The dark cousin of theme_light, with similar line sizes but a dark background. Useful to make thin coloured lines pop out.
    • theme_minimal() : A minimalistic theme with no background annotations.
    • theme_classic() : A classic-looking theme, with x and y axis lines and no gridlines.
    • theme_void() : A completely empty theme.
  • ggthemen 패키지 테마

    • theme_base() : A theme resembling the default base graphics in R. See also theme_par.
    • theme_calc() : A theme based on LibreOffice Calc.
    • theme_economist() : A theme based on the plots in the The Economist magazine.
    • theme_excel() : A theme replicating the classic ugly gray charts in Excel.
    • theme_few() : A theme from Stephen Few’s “Practical Rules for Using Color in Charts”.
    • theme_fivethirtyeight() : A theme based on the plots at fivethirtyeight.com.
    • theme_gdocs() : A theme based on Google Docs.
    • theme_hc() : A theme based on Highcharts JS.
    • theme_par() : A theme that uses the current values of the base graphics parameters in par.
    • theme_pander() : A theme to use with the pander package.
    • theme_solarized() : A theme using the solarized color palette.
    • theme_stata() : Themes based on Stata graph schemes.
    • theme_tufte() : A minimal ink theme based on Tufte’s The Visual Display of Quantitative Information.
    • theme_wsj() : A theme based on the plots in the The Wall Street Journal.

이번 포스팅에서는 산점도에 대해서 살펴보는 것으로 마무리하고, 다음 포스팅에서 막대그래프, 선그래프, 히스토그램, 상자수염그림 등을 추가로 소개하겠습니다.

[1] 출처 : http://bloggotype.blogspot.kr/2016/08/holiday-notes2-grammar-of-graphics.html

[2] 출처 : https://m.blog.naver.com/PostView.nhn?blogId=definitice&logNo=221127682474&targetKeyword=&targetRecommendationCode=1&proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F

Written on April 2, 2018