이 블로그와 아주 좋은 친구인 위키피디아는 상자-수염 그래프를 이렇게 설명합니다.
기술 통계학에서 '상자 수염 그림''box-and-whisker plot, box-and-whisker diagram) 또는 '상자 그림'(box plot, boxplot)은 수치적 자료를 표현하는 그래프이다.
이 그래프는 가공하지 않은 자료 그대로를 이용하여 그린 것이 아니라, 자료로부터 얻어낸 통계량인 5가지 요약 수치(다섯 숫자 요약, five-number summary)를 가지고 그린다.
이 때 5가지 요약 수치란 최솟값, 제 1사분위(Q1), 제 2사분위(Q2), 제 3사분위(Q3), 최댓값을 일컫는 말이다.
여기서 우리가 주목해야 하는 표현은 두 번째 문장에 나옵니다.
상자 수염 그래프는 가공 않은 자료 그러니까 전체 데이터(Raw Data)로 그리는 게 아니라 따로 계산한 통계량을 가지고 그립니다.
그런데 R에서 (ggplot2로) 상자 수염 그래프를 그릴 때는 전체 데이터를 활용하는 게 일반적입니다.
만약 미리 계산한 결과만 알고 있을 때는 어떻게 상자-수염 그래프를 그려야 할까요?
제목에 나온 것처럼 힌트는 stat='identity' 옵션입니다.
geom_col() 함수가 등장하기 전에는 막대 그래프를 그릴 때 geom_bar(stat='identity')라고 썼던 걸 기억하는 분이 계실 겁니다.
구글에 stat = 'idenity'라고 검색해 보면 막대 그래프 이미지가 주욱 뜰 정도입니다.
상자-수염 그래프도 똑같이 geom_boxplot(stat='identity')라고 쓰면 미리 계산한 결과를 가지고 상자-수엽 그래프를 그릴 수 있습니다.
stat='identity'라는 옵션 자체가 R에서 뭔가 따로 계산하지 말고 현재 있는 자료를 그대로 쓰라는 뜻이거든요.
그래서 R에 자료 집계를 맡길 때는 그냥 geom_bar()라고 쓰지만 따로 자료를 입력했을 때는 geom_bar(stat='identity')라고 썼던 겁니다.
그러면 실제로 상자-수염 그래프를 그리는 연습을 해보겠습니다.
제일 먼저 할 일은 늘 그렇듯 tidyverse 패키지 불러오기.
혹시 왜 ggplot2가 아니라 tidyvese를 불러오는지 궁금하신 분도 계실 겁니다.
tidyverse는 ggplot2를 비롯해 △dplyr △forcats △purrr △readr △stringr △tibble △tidyr 등을 담고 있는 패키지 '묶음'입니다.
각 패키지가 서로 긴밀하게 엮어 있기 때문에 한번에 불러오는 편이 낫습니다.
아직 tidyvese 패키지를 설치하지 않으셨다면 이번 기회에 설치해 보시는 것도 좋습니다.
#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()
먼저 기존 방식대로 전체 데이터를 가지고 상자-수염 그래프를 그려보겠습니다.
표준 정규분포를 따르는 샘플 데이터를 만들어 상자-수염 그래프를 그리는 코드는 이렇게 쓸 수 있습니다.
tibble(
y=rnorm(n=100, mean=0, sd=1)
) %>%
ggplot(aes(y=y)) +
geom_boxplot()
혹시 이 코드가 전혀 이해가 가지 않으시는 분은 '최대한 친절하게 쓴 R로 그래프 그리기(feat. ggplot2)' 포스트가 도움이 될 수 있습니다.
이미 알고 있는 값으로 상자-수염 그래프를 그릴 때는 당연히 이미 알고 있는 값을 전부 알려줘야 합니다.
최솟값 0부터 각 사분위별로 25씩 늘어나서 최댓값이 100이 되는 자료를 일단 하나 만들어 놓겠습니다.
tribble(
~최솟값, ~x1분위, ~x2분위, ~x3분위, ~최댓값,
0, 25, 50, 75, 100
) -> sample
sample
## # A tibble: 1 x 5
## 최솟값 x1분위 x2분위 x3분위 최댓값
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0 25 50 75 100
그다음 위치별로 값을 지정합니다. 최솟값은 ymin, 1분위는 lower, 중간값(2분위)은 middle, 3분위는 upper, 최댓값은 ymax에 넣으면 됩니다.
이때 주의해야 할 건 x값을 따로 지정해주지 않으면 에러 메시지가 나온다는 것.
지금은 x에 어떤 값이 오든지 크게 관계가 없으니까 일단 0이라고 지정하겠습니다.
그러고 나서 stat='identity' 옵션으로 코드를 마무리하면 됩니다.
sample %>%
ggplot(aes(x=0,
ymin=최솟값,
lower=x1분위,
middle=x2분위,
upper=x3분위,
ymax=최댓값)) +
geom_boxplot(stat='identity')
ggplot2로 상자-수염 그래프를 그리면 최솟값·최댓값에 따로 표시를 해주지 않습니다.
이걸 표시하고 싶으실 때는 geom_errorbar() 함수를 적용하시면 됩니다.
이 함수를 나중에 쓰면 이 오차 막대가 그래프를 덮어 버립니다.
그러니 geom_boxplot()보다 앞에 이 함수를 쓰셔야 합니다.
sample %>%
ggplot(aes(x=0,
ymin=최솟값,
lower=x1분위,
middle=x2분위,
upper=x3분위,
ymax=최댓값)) +
geom_errorbar(width = .2) +
geom_boxplot(stat='identity')
'나는 전체 데이터는 없지만 각 분위값은 전부 알고 있다. 그런데 R에서 상자-수염 그래프를 그리는 법을 몰라 답답하다.'
이렇게 생각하면서 골머리를 앓고 계신 분께 작은 도움이 되기를 바라는 마음으로 글을 마칩니다.
그럼 모두들 Happy Charting -_-)/
댓글,