ggplot2는 dplyr등과 함께 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를 잊어버리고, 다시 displ과 hwy간의 산점도로 돌아와봅시다. 제목을 붙이고, 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를 이용하여 기본적인 그림은 그리실 수 있고, 다른 사람의 코드를 보고 파악하시는데 큰 어려움은 없으리라 생각됩니다.