ggplot2dplyr등과 함께 tidyverse라는 R 패키지 집합체에 속해있는 패키지입니다. tidyverse는 데이터 사이언스에 대한 Hadley Wickham의 철학이 담겨있는 패키지 집합체인데, 특히 ggplot2는 The Grammar of Graphics라는 책에 나온 원칙에 따라 그래프를 그릴 수 있게 해주는 패키지 입니다. 그런데 ggplot2를 처음 접하면 도대체 어떻게 그래프를 그리는 것인지 파악이 어려울 수 있습니다. 그래서 ggplot2가 작동하는 아주 기본적인 원리를 소개해드리려고 합니다. 여기 나온 예제는 https://r4ds.had.co.nz/index.html 에서 많은 부분 가져왔습니다.

기본 구조

ggplot2의 가장 기본적인 구성 요소는 ggplot()geom_xxxx() 형식의 이름을 가지고 있는 geom function 입니다. 각 요소는 + 기호로 연결됩니다. ggplot() 안에는 data= 구문으로 데이터셋 이름을 넣어주고, geom function의 종류는 그리려는 그래프의 종류를 의미합니다. 그리고 geom function 안에는 mapping=aes() 구문으로 그리려는 그래프의 x, y, 그 외의 매핑 정보를 입력합니다.

ggplot(data = <DATA>) + <GEOM_FUNCTION>(mapping = aes(<MAPPINGS>))

한가지 주의할 점은, 코드가 길어져서 다음 줄로 넘어가야 한다면, 반드시 +가 오른쪽 끝에 있어야 합니다.

ggplot2 패키지에 내장된 mpg 데이터셋을 이용해서 가장 간단한 산점도를 하나 그려보겠습니다.

library(ggplot2)
ggplot(data=mpg) + geom_point(mapping=aes(x=displ, y=hwy))

어떤 범주형 변수의 범주별로 다른 색깔의 점을 찍고 싶다면, 역시 mapping=aes() 구문 안에 지정하면 됩니다. 여기에서는 차종을 의미하는 class라는 변수를 이용해서, 차종별로 다른 색깔을 찍어봅시다.

ggplot(data=mpg) + geom_point(mapping=aes(x=displ, y=hwy, color=class))

모든 점을 파랗게 만들고 싶다면 어떻게 해야할까요? 아래의 두 그래프와 코드의 차이점을 잘 보세요.

ggplot(data=mpg) + geom_point(mapping=aes(x=displ, y=hwy, color="blue"))

ggplot(data=mpg) + geom_point(mapping=aes(x=displ, y=hwy), color="blue")

흑백으로 프린트할 경우 색깔보다는 shape으로 구분하는 게 좋겠지요. 그런데 6개를 초과하는 shape은 ggplot2가 자동으로 없애버립니다. 그럴 경우 scale_shape_manual() 함수를 추가해서 수동으로 shape을 지정해줘야 합니다.

ggplot(data=mpg) + geom_point(mapping=aes(x=displ, y=hwy, shape=class))
## Warning: The shape palette can deal with a maximum of 6 discrete values because
## more than 6 becomes difficult to discriminate; you have 7. Consider
## specifying shapes manually if you must have them.
## Warning: Removed 62 rows containing missing values (geom_point).

ggplot(data=mpg) + geom_point(mapping=aes(x=displ, y=hwy, shape=class)) + 
  scale_shape_manual(values=1:7) 

아예 class 별로 산점도를 따로따로 그리고 싶을 수도 있죠. 그럴 경우 facet_wrap() 함수를 뒤에 +로 연결해줍니다.

ggplot(data=mpg) + geom_point(mapping=aes(x=displ, y=hwy)) +
  facet_wrap(~class, nrow=2)

잠시 class를 잊어버리고, 다시 displhwy간의 산점도로 돌아와봅시다. 제목을 붙이고, x축, y축 범위나 레이블을 바꾸거나 tick mark의 값을 지정하는 것 모두 아래와 같이, 기본 형태에 +로 해당 요소들을 덧붙여주는 식으로 이루어집니다.

ggplot(data=mpg) + geom_point(mapping=aes(x=displ, y=hwy)) + 
  ggtitle("Engine displacement vs. Highway MPG") +
  xlab("Highway MPG") + ylab("Engine displacement (liters)") +
  scale_x_continuous(limits=c(1, 7), breaks=c(1:7)) +
  scale_y_continuous(limits=c(10, 50))

\(~\)

이제 다른 종류의 geom function들을 살펴보겠습니다. 먼저 스무딩한 곡선을 보여주는 geom_smooth() 입니다.

ggplot(data=mpg) + geom_smooth(mapping=aes(x=displ, y=hwy)) 
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

이 그래프 역시, 범주형 변수의 범주 별로 다른 곡선을 그릴 수 있습니다. 색깔을 달리하거나, 선의 모양을 달리할 수 있습니다. geom_point()와 마찬가지로 mapping=aes() 안에 지정해줍니다.

ggplot(data=mpg) + geom_smooth(mapping=aes(x=displ, y=hwy, color=drv)) 
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

ggplot(data=mpg) + geom_smooth(mapping=aes(x=displ, y=hwy, linetype=drv)) 
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

만약, 원 자료의 산점도와 스무딩 곡선을 한 그래프에 같이 보여주려면 어떻게 하면 될까요? +로 두가지 geom function을 다 붙여주면 됩니다.

ggplot(data=mpg) + geom_point(mapping=aes(x=displ, y=hwy)) +
  geom_smooth(mapping=aes(x=displ, y=hwy))
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

그런데 두개의 geom function 안에 똑같은 구문들이 들어가있죠? 이렇게 공통된 aesthetic 요소가 있으면, ggplot() 안으로 옮겨주어도 됩니다. ggplot() 안에서 정의한 mapping은 global mapping이라고 해서, 그 뒤에 따라 붙는 모든 geom function에 공통으로 적용됩니다. 즉, 아래의 코드는, 위의 그래프와 완전히 똑같은 그래프를 그려냅니다.

ggplot(data=mpg, mapping=aes(x=displ, y=hwy)) + geom_point() +
  geom_smooth()
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

그런데 global mapping을 정의했어도, local mapping을 추가로 적용할 수도 있습니다.

ggplot(data=mpg, mapping=aes(x=displ, y=hwy)) + geom_point(mapping=aes(color=class)) +
  geom_smooth()
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

이번엔 histogram을 그리는 geom function입니다.

ggplot(data=mpg) + geom_histogram(mapping=aes(x=hwy))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

histogram은 bin 사이즈에 따라 달라보일 수 있기 때문에 bin 사이즈를 정하는 옵션을 알아두면 좋을 듯 합니다.

ggplot(data=mpg) + geom_histogram(mapping=aes(x=hwy), binwidth = 2)

histogram 역시 범주형 변수의 범주에 따라 각각 따로 그릴 수 있습니다. 단, 겹쳐보이기 때문에 투명도를 조정해서 서로 비쳐보이게 하는 것이 좋습니다.

ggplot(data=mpg) + 
  geom_histogram(mapping=aes(x=hwy, fill=drv), binwidth = 2, alpha=0.5, position="identity")

geom_boxplot() 을 이용하여 box plot도 그릴 수 있습니다.

ggplot(data=mpg) + geom_boxplot(mapping=aes(x=class, y=hwy))

좀 정신이 없으니, hwy의 중앙값 기준으로 정렬하는 코드입니다.

ggplot(data=mpg) + geom_boxplot(mapping=aes(x=reorder(class, hwy, FUN=median), y=hwy))

class 이름이 길어서 옆으로 눞이는 것이 좋을 수도 있겠죠. coord_flip() 을 뒤에 연결해주면 됩니다.

ggplot(data=mpg) + geom_boxplot(mapping=aes(x=reorder(class, hwy, FUN=median), y=hwy)) + coord_flip()

이번에는 범주형 변수의 범주별 카운트를 보여주는 bar plot을 그리는 방법을 보여드리겠습니다. ggplot2 에 내장된 diamonds 데이터에서 각 cut 별 다이아몬드의 갯수를 나타내는 barplot 입니다.

ggplot(data=diamonds) + geom_bar(mapping=aes(x=cut))

각 컷 별로 투명도 (clarity) 의 분포를 보고 싶으면, 아래와 같이 mapping=aes() 안에 fill 구문으로 지정해줍니다.

ggplot(data=diamonds) + geom_bar(mapping=aes(x=cut, fill=clarity))

마지막으로 선 그래프입니다. ggplot2에 내장된 economics 데이터는 시계열 자료입니다. 날짜를 x축으로 하고, 날짜가 지남에 따라 실업률이 변하는 추세를 선 그래프로 그리는 코드입니다.

economics
## # A tibble: 574 x 6
##    date         pce    pop psavert uempmed unemploy
##    <date>     <dbl>  <dbl>   <dbl>   <dbl>    <dbl>
##  1 1967-07-01  507. 198712    12.6     4.5     2944
##  2 1967-08-01  510. 198911    12.6     4.7     2945
##  3 1967-09-01  516. 199113    11.9     4.6     2958
##  4 1967-10-01  512. 199311    12.9     4.9     3143
##  5 1967-11-01  517. 199498    12.8     4.7     3066
##  6 1967-12-01  525. 199657    11.8     4.8     3018
##  7 1968-01-01  531. 199808    11.7     5.1     2878
##  8 1968-02-01  534. 199920    12.3     4.5     3001
##  9 1968-03-01  544. 200056    11.7     4.1     2877
## 10 1968-04-01  544  200208    12.3     4.6     2709
## # ... with 564 more rows
ggplot(economics, aes(x=date, y=unemploy)) + geom_line()

이 정도면 ggplot2를 이용하여 기본적인 그림은 그리실 수 있고, 다른 사람의 코드를 보고 파악하시는데 큰 어려움은 없으리라 생각됩니다.