ggplot2
팩키지로 데이터를 시각화한다.- 고난도 복잡한 시각화 산출물을
ggplot2
팩키지를 활용하여 단계적으로 구축한다.- Figshare surveys.csv, mammals data 데이터를 활용하여 시각화를 실습한다.
ggplot2
로 시각화하는 이유R에 기본적으로 시각화를 위한 다양한 기능이 존재하지만, ggplot2
가 기본으로 제공되는 R 시각화 기능 위에 더욱 강력한 기능을 제공한다. 기본적인 R 기능도 상대적으로 다른 소프트웨어 서비스나 제품과 비교가 되지 않을 정도록 강력하다. 이런 점에서 R은 참 겸손하다는 생각이 든다.
# read_csv로 csv 파일을 불러 읽어온다.
library(readr)
# 시각화를 위한 ggplot2 팩키지를 불러 읽어온다.
library(ggplot2)
# ggplot2에 입력데이터로 사용될 데이터를 전처리한다.
library(dplyr)
# 데이터를 불러온다.
download.file(url = "https://ndownloader.figshare.com/files/2292169",
destfile = "data/portal_data_joined.csv")
surveys_dat <- read_csv('data/portal_data_joined.csv')
figshare2 사이트에서 데이터를 가져온다. surveys.csv
데이터는 포획된 동물에 관한 측정정보가 담겨 있다.
다운로드 받은 데이터에 대한 요약정보를 summary
함수를 사용해서 살펴본다.
record_id month day year plot_id
Min. : 1 Min. : 1.000 Min. : 1.0 Min. :1977 Min. : 1.00
1st Qu.: 8964 1st Qu.: 4.000 1st Qu.: 9.0 1st Qu.:1984 1st Qu.: 5.00
Median :17762 Median : 6.000 Median :16.0 Median :1990 Median :11.00
Mean :17804 Mean : 6.474 Mean :16.1 Mean :1990 Mean :11.34
3rd Qu.:26655 3rd Qu.:10.000 3rd Qu.:23.0 3rd Qu.:1997 3rd Qu.:17.00
Max. :35548 Max. :12.000 Max. :31.0 Max. :2002 Max. :24.00
species_id sex hindfoot_length weight
Length:34786 Length:34786 Min. : 2.00 Min. : 4.00
Class :character Class :character 1st Qu.:21.00 1st Qu.: 20.00
Mode :character Mode :character Median :32.00 Median : 37.00
Mean :29.29 Mean : 42.67
3rd Qu.:36.00 3rd Qu.: 48.00
Max. :70.00 Max. :280.00
NA's :3348 NA's :2503
genus species taxa plot_type
Length:34786 Length:34786 Length:34786 Length:34786
Class :character Class :character Class :character Class :character
Mode :character Mode :character Mode :character Mode :character
데이터셋에 일부 결측정보가 summary
실행결과를 보여주고 있어, 이를 제거한다. 각 변수마다 결측치가 있어 이를 한땀 한땀 정성스럽게 결측정보를 제거하는 대신에 dplyr
파이프 연산자를 사용하여 일괄적으로 처리한다.
개체수가 적은 종이 많아서, 개체수 기준 10 보다 적은 종은 제거하기로 한다. 먼저 group_by
함수로 개체종을 그룹으로 군집화하고, tally
함수로 각 종별로 개체수를 세고, 내부인자로 sort=TRUE
를 넣어서 내림차순으로 정렬한다.
species_counts <- surveys_complete %>%
group_by(species_id) %>%
count(sort=TRUE)
tail(species_counts)
# A tibble: 6 x 2
# Groups: species_id [6]
species_id n
<chr> <int>
1 PH 31
2 PI 8
3 RO 8
4 OX 5
5 PX 2
6 RX 2
개체수가 10개 미만인 종을 제거하고, 시각화를 위한 기본 데이터셋 준비를 완료한다.
weight
를 예측변수 x
위치에 두고, 종속변수 hindfoot_length
를 y
에 두고 R 기본 시각화 산점도를 도식화해 보자.
ggplot2
로 시각화ggplot2
를 사용해서 R 기본 내장된 시각화 기능을 대신하여 동일한 작업을 수행할 수 있다.
데이터프레임 데이터에서 복잡하고 정교한 시각화 산출물을 ggplot2
팩키지로 생성할 수 있다. 기본설정만으로 최소의 노력으로 출판 품질 시각화 산출물을 만들어 낼 수 있다.
ggplot2
시각화 산출물은 시각화 요소를 차곡차곡 추가해 나가면서 만들어 간다.
ggplot2
시각화 산출물은 다음 단계로 만들어 나간다:
data
인자로 특정 데이터프레임과 플롯을 묶어 연결시킨다.aes
로 정의해서, 플롯축에 데이터 변수를 매핑하고, 크기, 모양, 색상 등을 시각화한다.geoms
을 사용해서 플롯에 반영한다. 플롯에 geoms
를 추가하는데 +
연산자를 사용한다::주의 사항
ggplot
함수에 넣은 어떤 것도 추가한 geom
계층을 통해 반영된다. 즉, 보편적인 플롯 설정이다.aes()
에 설정한 x
축, y
축도 여기에 포함된다.ggplot(data = surveys_complete, aes(x = weight, y = hindfoot_length)) +
geom_point(alpha=0.1, color = "blue")
각 종별로 체중 분포를 시각화한다.
상자그림에 점을 추가해서, 특이한 관측점과 많이 관측된 측정값을 볼 수 있다.
ggplot(data = surveys_complete, aes(x = species_id, y = weight)) +
geom_jitter(alpha = 0.3, color = "tomato") +
geom_boxplot(alpha = 0)
상기 시각화 산출물에서 상자그림이 지터 계층 위에 놓인 방식에 주목한다. geoms
순서를 조정하고, 투명도를 조절해 플롯에 계층을 쌓는 방식을 제어한다.
도전과제
hindfoot_length
에 대한 상자그림을 생성한다.
각 종별로 년도별 개체수를 계산한다. 이 작업을 수행하기 위해서는 먼저 데이터를 그룹집단화하고, 각 그룹마다 해당 레코드 개수를 센다.
x
축에 연도, y
축에 개수를 놓고 직선으로 시간에 따라 경과한 정보를 시각화한다.
불행하게도, 상기 그래프는 원하는 바가 아닌데, 이유는 모든 종에 대해 데이터를 시각화하게 명령어를 전송했기 때문이다. species_id
로 시각화한 데이터를 쪼갠 후에 ggplot
명령어로 시각화하게 한다.
색상을 추가하게 되면, 그래프에서 개체를 식별하게 된다.
ggplot(data = yearly_counts, aes(x = year, y = n, group = species_id, color = species_id)) +
geom_line()
ggplot
에는 측면 보여주기(faceting) 라는 특수한 기능이 있어서, 특정 요인에 따라 그래프 하나를 다수 그래프로 쪼갤 수 있다. 예를 들어, 각 종마다 시계열 그래프를 별도로 도식화할 수 있다.
ggplot(data = yearly_counts, aes(x = year, y = n, color = species_id)) +
geom_line() + facet_wrap(~species_id)
관측된 각 개체 성별에 따라 그래프에 직선을 쪼개고자 한다. 이 작업을 수행하려면, 성별로 그룹을 만들어 데이터프레임에 개수를 세어야 된다.
도전과제
- 데이터프레임을 필터링해서 “F” 혹은 “M” 값을 갖는 레코드만 갖도록 작업한다.
- 연도(
year
), 개체 신원정보(special_id
), 성(sex
) 별로 그룹을 만든다.
- (개별 그래프 내부에) 성별로 쪼개는 측면보여주기 플롯을 생성한다.
ggplot(data = yearly_sex_counts, aes(x = year, y = n, color = species_id, group = sex)) + geom_line() + facet_wrap(~ species_id)
- 논문출판으로 흰색 배경이 좀더 가독성을 좋게 한다.
theme_bw()
함수를 사용해서 흰색 배경을 적용한다.ggplot(data = yearly_sex_counts, aes(x = year, y = n, color = species_id, group = sex)) + geom_line() + facet_wrap(~ species_id) + theme_bw()
- 종대신에 성별로 색상을 입혀서 그래프 가독성을 좋게 만들 수 있다. (종은 이미 별도 그래프로 시각화되어서, 더 잘 식별하게 만들 필요는 없다.)
ggplot(data = yearly_sex_counts, aes(x = year, y = n, color = sex, group = sex)) + geom_line() + facet_wrap(~ species_id) + theme_bw()
- 연도에 걸쳐 각 종별로 평균 체중을 시각화한다.
yearly_weight <- surveys_complete %>% group_by(year, species_id, sex) %>% summarise(avg_weight = mean(weight, na.rm = TRUE)) ggplot(data = yearly_weight, aes(x=year, y=avg_weight, color = species_id, group = species_id)) + geom_line() + theme_bw()
- 시각화를 왜 이런 단계를 밟아서 도식화 절차를 거친다고 생각하는가?
- 수컷과 암컷 체중이 상당히 차이가 나서 성별로 별도 시각화를 수행한다.
ggplot(data = yearly_weight, aes(x=year, y=avg_weight, color = species_id, group = species_id)) + geom_line() + facet_wrap(~ sex) + theme_bw()
지금까지 시각화 결과가 상당히 좋았지만, 아직 출판하기에는 많이 부족하다. 시각화 산출물 결과를 향상할 수 있는 다른 방법은 무엇이 있을까?
ggplot2
컨닝쪽지(cheat sheet)를 참조하고, 적어도 세가지 아이디어를 적어본다.
x
축과y
축에 ‘year’와 ’n’ 보다 더 많은 정보를 전달하도록 변경하고, 그래프에 제목을 추가한다.ggplot(data = yearly_sex_counts, aes(x = year, y = n, color = sex, group = sex)) + geom_line() + facet_wrap(~ species_id) + labs(title = 'Observed species in time', x = 'Year of observation', y = 'Number of species') + theme_bw()
이제 좀더 나아져서 훨씬 더 많은 정보를 주는
x
,y
축 명칭으로 바꿨지만, 가독성이 떨어지고 있다. 글자 크기를 변경하고 글자체도 변경한다.ggplot(data = yearly_sex_counts, aes(x = year, y = n, color = sex, group = sex)) + geom_line() + facet_wrap(~ species_id) + labs(title = 'Observed species in time', x = 'Year of observation', y = 'Number of species') + theme(text=element_text(size=16, family="Arial")) + theme_bw()
조작을 한 다음에,
x
축이 여전히 적절한 가독성을 전달하고 있지 않음을 볼 수 있다. 라벨 방향을 변경해서 서로 겹쳐지지 않도록 수평 혹은 수직방향으로 바꾼다. 90도 각도를 사용하거나, 대각선 방향으로 라벨방향을 변경하도록 적절한 각도로 바꾸는 실험을 진행한다.ggplot(data = yearly_sex_counts, aes(x = year, y = n, color = sex, group = sex)) + geom_line() + facet_wrap(~ species_id) + theme_bw() + theme(axis.text.x = element_text(colour="grey20", size=12, angle=90, hjust=.5, vjust=.5), axis.text.y = element_text(colour="grey20", size=12), text=element_text(size=16, family="Arial")) + labs(title = 'Observed species in time', x = 'Year of observation', y = 'Number of species')
이제, 라벨을 키워서 가독성이 더 좋아졌지만, 개선할 여지는 남아 있다. 시간을 5분만 더 들여서 더 나은 시각화 산출물을 만들어 내도록 하나 혹은 두가지 작업을 시도해 본다.
ggplot2
컨닝 쪽지를 사용해서, 상기 시각화 산출물에 적용할 영감을 받아본다.다음에 몇가지 생각난 것이 있다:
- 선 두께를 변경할 수 있는지 살펴본다.
- 범례(legend) 명칭을 변경할 방법을 살펴본다. 범례 라벨은 어떤가?
- 외양을 좋게 하는데 다른 색상 팔레트를 사용한다. (http://www.cookbook-r.com/Graphs/Colors_(ggplot2)/)
완벽한 시각화 산출물이 도출되면, 선호하는 그림파일 형식으로 저장한다. 그림 폭과 높이를 지정해서 크기를 쉽게 변경한다.
Error in plot.new() : figure margins too large
시각화 문제 해결그림에 좌우상하 여백(margin)이 맞지 않는 오류가 발생하는 경우 좌우상하 여백을 기본설정
par(mar=c(1,1,1,1))
명령어를 통해 해결한다.