-
ggplot2 : R 시각화R 이모저모 2019. 6. 10. 19:06
ggplot2를 활용한 시각화
이번 포스팅에서는 R에서 가장 보편적으로 쓰이는 시각화 툴 중 하나인 ggplot2에 대해서 다뤄보고자 합니다. 비록 ggplot2가 속도면에서 문제가 있어 다른 대안을 찾는 추세긴 하나, 여전히 쉽고 강력한 리포팅 기능을 자랑한다는 점은 부인할 수 없는 만큼 기초를 배워두면 R을 사용하는데 있어 큰 도움이 될 것입니다.
0. ggplot의 구조
ggplot은 비교적 직관적인 구조로 이루어져 있습니다. 먼저 ggplot()이라는 함수로 plot을 선언한 후 안에 aes(aesthetic) 함수를 이용해서 원하는 값(예 : 산점도의 x, y좌표)을 넣습니다. 이 때 직접 데이터를 집어넣어도 되며, ggplot함수에서 데이터를 선언하여 앞으로 쓸 데이터를 지정해줘도 됩니다. 데이터를 선언할 경우, dplyr의 함수들과 유사하게 변수 이름만으로 데이터를 불러올 수 있는 장점이 있습니다. 이후 dplyr의 체인과 유사하게 +를 통해서 원하는 기능을 더해가서 그림을 완성하게 됩니다.
ggplot 구조 위와 같은 과정을 거쳐 만들어진 ggplot 객체는 list형식으로 저장되며, 이 객체에 + 연산자를 더해서 기능을 추가할 수도 있습니다. 그럼 이제 ggplot에서 자주 쓰이는 5가지 plot형식에 대해 예제를 통해 알아보도록 하겠습니다.
1. Scatter Plot
Scatter Plot(산점도)는 x, y 두 개의 데이터가 필요한 그림으로, ggplot에서는 geom_point를 이용해서 정의합니다. 위 사진에서 든 예시 코드를 실행시키면 다음과 같은 그림이 나오게 됩니다.
geom_point를 이용한 iris데이터 산점도 여기에 color, 즉 색깔을 변수별로 넣으려면 aes()안에 color = 변수명으로 지정해야합니다.
Species 변수를 기준으로 색상을 더한 그래프 만약 데이터가 순차적인 것(ex : 시계열 데이터)이라면, 점들을 선으로 이어볼 수도 있을 것입니다. 이때는 위의 그래프 객체 p에 geom_line을 더해서 간단히 그릴 수 있습니다.
geom_line으로 선을 더한 그래프 위 그래프처럼 하나의 기능이 아닌 두 개의 기능(point와 line)을 붙였을 때, ggplot()함수 부분에서 모든 aes를 정의하지 않고 따로 정의함으로서 다른 색을 입히는 것이 가능합니다. 아래 그래프는 geom_point에는 색깔을 주지 않고 선에만 색깔을 준 그래프입니다.
geom_line에만 color aesthetic이 적용된 그래프 또한 aesthetic함수 밖에서 색상을 지정할 수 있으며, 이 경우 legend에 나오는 일 없이 수동으로 지정된 색상이 나오게 됩니다.
점은 파란색, 선은 빨간색으로 지정한 그래프 geom_smooth는 말 그대로 smooth 한 추세선을 그려주며, 기본은 loess 방법을 따라갑니다. 만약 직선 추세선을 얻고 싶다면 method 파라미터를 lm으로 바꿔주면 됩니다.
기본 방법론(loess)으로 근사된 곡선 직선(linear model, lm)으로 근사된 추세선 이 때, 선에 생기는 회색 근사지역을 없애고 싶다면 se 파라미터를 FALSE로 지정하면 됩니다.
se = FALSE로 confidence interval이 없어진 그림 geom_text 함수는 그래프에 글자를 추가해주며, 이는 좌표가 일정한 산점도에서 별도의 aesthetic 지정 없이 유용하게 쓸 수 있습니다.
Species 변수를 문자로 넣은 그래프 2. Histogram
R에서 히스토그램은 hist 함수를 이용해서 간단히 그릴 수 있으나, ggplot의 geom_histogram을 이용하면 좀 더 깔끔하게 그릴 수 있습니다. 기본적으로 히스토그램은 단변수의 빈도 분포를 나타내기에, aes 부분에 하나의 인풋(x)만을 받게 됩니다.
iris데이터의 Sepal Length 변수 히스토그램 geom_histogram 함수의 bindwidth파라미터를 조정하면 히스토그램의 간격을 조정할 수 있습니다. 위의 그래프의 bin 간격을 0.1로 조정하면 아래와 같은 그래프가 나옴을 확인할 수 있습니다.
binwidth = 0.1로 조정된 히스토그램 3. Bar plot
바 그래프는 종류별 빈도를 나타내기에 가장 편한 그래프로, 주로 집단별로 수치적 데이터가 얼마나 있는지를 표시할 때 많이 사용합니다. ggplot에선 geom_bar를 사용하여 연출하며, 하나의 변수를 입력할 경우 빈도 기반으로(=히스토그램) 나오게 됩니다.
iris데이터의 sepal length를 barplot으로 표현한 그림 바 그래프 또한 변수를 기준으로 색칠할 수 있으며, color 옵션을 통해 색칠 시 아래와 같은 그래프가 나오게 됩니다.
colored by Species 여기서 확인할 수 있는 점은, color의 경우 외곽선을 칠한다는 것입니다. 바의 안쪽을 칠하고 싶다면 fill 옵션을 사용하여야 합니다.
filled by Species 위 그림보다 훨씬 깔끔하게 색칠된 그래프를 볼 수 있습니다. 또한 두 그래프에서 보듯 단일 변수 바 그래프에서 색깔 aesthetic을 집어넣을 시 바를 분리해서 색칠하는 식으로 색칠됨을 알 수 있습니다. 이 색칠 방식을 변경하려면 geom_bar의 position 파라미터를 fill이나 dodge로 바꿈으로서 해결할 수 있습니다.
position = fill position = dodge 2가지 변수를 넣는 경우, y변수를 x변수 기준으로 통합하여 바 그래프를 그리며, 이 때 geom_bar의 stat을 identity로 설정해줘야만 합니다.
Species 변수 기준으로 정리된 Sepal.Length의 그래프 4. density plot
밀도 그래프는 1차원과 2차원이 있으며, 1차원의 경우 geom_density 함수를 이용해서 구현할 수 있습니다. 이 때 밑(y = 0)에 불필요한 선이 그려지는데, 이를 지우고 싶다면 geom_line을 이용해서 그리면 간단히 해결할 수 있습니다.
geom_density를 이용한 그래프 geom_line(stat = 'density')를 이용한 그래프, 밑에 줄이 사라짐 2차원 밀도의 경우 흔히 말하는 contour plot과 동일하며, 어느 곳에 두 변수가 밀집되어 있는지를 표현해주는 그래프입니다. geom_density2d를 활용하여 그릴 수 있습니다.
geom_density2d을 활용한 그래프 실제 점 분포와 밀도 그래프를 같이 보고 싶다면 위 그래프에 geom_point를 이용해서 산점도를 추가하는 것으로 볼 수 있습니다.
산점도가 추가된 밀도 그래프, 위의 그림에서 보다시피 실제로 점이 모여있는 곳에 그래프가 형성됨을 알 수 있습니다.
5. Boxplot
박스플랏은 그룹별 한 연속변수의 평균, 4분위, 최소/최대값의 차이를 보기 위해 그리며, geom_boxplot을 이용해서 그릴 수 있습니다.
Species 변수별로 나눈 Sepal.Length의 박스플랏 여타 그래프와 같이, color aesthetic을 활용해서 색깔을 입혀줄 수 있습니다.
colored by Species 6. 테마와 라벨
지금까지 기본적인 plot들의 형태에 대해 알아보았습니다. 이제 만들어진 그래프를 좀 더 예쁘게 꾸미는 방법인 테마와, 제목을 붙여주는 라벨에 대해서 알아보겠습니다. 먼저 전체 그래프 테마의 경우 theme_~로 시작하는 여러 함수들로 제공되며, 제가 자주 사용하는 테마는 theme_bw입니다.
theme_bw로 바뀐 그래프 다음으로 그래프의 제목을 붙여보겠습니다. ggplot은 labs혹은 ggtitle 함수로 제목을 넣을 수 있으며, 저는 ggtitle로 넣어보겠습니다. ggtitle은 메인 제목과 서브 제목으로 나눠서 구성할 수 있으며, 아래와 같은 결과가 나오게 됩니다.
그래프 제목 설정 위와 비슷한 원리로, x축과 y축의 제목 또한 변경할 수 있습니다. xlab은 x축, ylab은 y축의 제목을 담당하는 함수입니다.
x,y축의 제목이 변경된 그래프 위와 같은 간단한 그래프의 경우 제목이 잘 보이지만, 복잡한 그래프거나 다른 사정으로 그래프의 어떤 요소를 지우고 싶을 수도 있습니다. 이 경우 theme함수의 여러 파라미터를 사용해서 지울 수 있으며, 방법은 원하는 요소 = element_blank()입니다. 예를 들어 축의 제목을 지우고 싶다면 아래와 같이 axis.title = element_blank()로 설정하면 됩니다.
축의 제목이 지워진 그래프 마찬가지로 tick(눈금)을 없애고 싶다면 axis.ticks, x축의 글자들만 없애고 싶다면 axis.text.x를 지정해주면 됩니다.
눈금이 없어진 그래프 x축의 글자가 없어진 그래프 그렇다면 만약 그래프 범례를 제거하고 싶다면 어떤 작업을 해야할까요? 이 때는 약간 특이하게 legend.position = 'none'으로 지정해주어야합니다.
범례가 사라진 그래프 물론, theme 함수는 단순히 요소를 지우는 것으로만 쓰는 것은 아닙니다. 글자의 경우 element_text함수를 이용해서 여러 속성을 지정해 줄 수 있는데, 예를 들어 그래프의 제목을 볼드체로 바꾸고 싶다면 plot.title = elemt_text(face = 'bold')로 지정하여 바꿀 수 있습니다.
볼드처리된 그래프 제목 7. coord : Coordinate system
ggplot은 그래프의 구조를 쉽게 반전시키기 위해서 coordinate system을 coord_함수로 제공합니다. 예를 들어, 위 그래프의 x축과 y축을 서로 바꾸고 싶다면 coord_flip 함수를 사용하여 바꿀 수 있습니다.
x,y축이 반전된 그래프 coord_polar는 이름 그대로 그래프를 원형배치로 바꿔주는 함수로, 파이차트, 스파이더 차트 등을 생성할 때 유용하게 사용할 수 있습니다.
점이 원형으로 배치된 그래프 coord_cartesian은 x와 y의 범위를 지정해줄수 있는 함수로, 그래프의 특정 부분만 제한하여 보고 싶을 때 유용하게 사용할 수 있습니다.
x축을 6 ~ 7 범위로 제한한 그래프 y축을 3 ~ 6범위로 제한한 그래프 8. Facet
facet은 범주형 변수를 기준으로 그래프를 쪼개주는, 일종의 par기능을 해주는 함수입니다. ggplot의 facet이 par보다 더 유용한 점은 하나의 그래프 제목과 범례 안에 여러 그래프를 담을 수 있다는 점입니다. facet_grid 함수는 lm이나 기타 표현식을 받는 함수들과 동일하게 y ~ x 형식으로 사용 가능하며, ~y를 할 경우 칼럼단위로 나눠진 그래프를, y~.를 할 경우 행 단위로 나누어진 그래프를 출력합니다.
Species 변수 기준으로 생성된 column facet grid Species 변수 기준으로 생성된 row facet grid 언뜻 보기에 굉장히 편한 기능이지만, 단점은 위에서 보다시피 facet으로 나눠진 축은 하나의 축값만 제공해준다는 것입니다. 예를 들어, 아래와 같이 그룹별로 편차가 굉장히 심한 데이터(생성된 df는 g가 a인 경우 {50 ~ 200, 3~5}, b인 경우 {1 ~ 20, 10 ~ 70}, c인 경우 {500 ~ 2000, 0.5 ~ 1.2}로 x,y값의 편차가 그룹별로 매우 크다.)의 경우, 다른 y축을 써야하지만 facet_grid는 그 기능을 제공하지 않고 있는 것을 볼 수 있습니다.
facet_grid로 생성된 그래프 축이 그룹 하나만 표현해주고 있다. 위와 같은 경우 facet_wrap을 한 후 scales = 'free' 옵션을 사용하여 간단히 표현할 수 있습니다. (원래 facet_grid도 제공해야하나 무슨 이유에서인지 작동하지 않음)
scales = free로 인해 각자 다른 축을 가진 그래프 9. Coloring
지금까지 본 예제들은 거의 다 색깔을 변수, 그것도 범주형 변수로 지정한 경우였습니다. 만약 연속형 변수로 색깔을 지정하면 어떻게 될까요? 답은 gradient(스펙트럼 형식)한 색깔로 칠해진다 입니다.
연속형 변수 Petal.Length로 칠한 그래프 위와 같이 aesthetic으로 지정된 색깔이 맘에 들지 않을 경우, scale_color_manual 함수를 이용하여 수동으로 바꿔줄 수 있습니다.
aesthetic 색상 대신 rainbow(3)으로 생성된 색깔을 넣은 그래프 혹은 미리 설정된 여러 palette를 가져와서 사용하는 방법이 있는데, 이 경우 scale_color_brewer함수를 사용합니다.
Dark2 팔렛트를 사용한 그래프 앞서 설명한 gradient한 색상 또한 조정할 수 있으며, 낮은 쪽과 높은 쪽의 색상만 조정하는 scale_color_gradient 함수와, 중간 색상도 조정가능한 scale_color_gradient2가 있습니다. 2번째 함수는 mid, midpoint를 사용하여 중간지점 설정 및 중간 색상 설정이 가능합니다.
scale_color_gradient로 만든 그래프 scale_color_gradient2로 만든 그래프 10. Shape
당연하게도, ggplot에서는 점의 모양이 변경 가능합니다. aesthetic에 넣을 경우 변수에 따라 정해진 preset대로, 밖에 넣을 경우 0 ~ 25번에 설정된 모양대로 변경됩니다.
shape=0(네모)인 그래프 shape = 15(속이 찬 네모)인 그래프 Species변수에 따라 모양이 다른 그래프 11. Size와 alpha
ggplot은 size와 alpha라는 인풋 또한 제공합니다. size는 말 그대로 해당 기능으로 나오는 그림의 크기를, alpha는 투명도를 나타냅니다. 예를 들어 geom_point에 size와 alpha를 조절할 시 아래와 같은 그림이 나오게 됩니다.
점크기를 5로 키운 그래프 점크기를 5로 키우고 투명도를 0.3으로 낮춘 그래프 번외. stat_ellipse로 원그리기
군집 분석을 한 후, 해당 군집변수로 묶인 지역을 나타내고 싶을 때 사용할 수 있는 테크닉으로 stat_ellipse가 있습니다. 아래 그래프의 경우 별다른 군집화 없이 Species 기준으로 나누어 겹치는 부분이 많으나, 군집분석을 통해 잘 나누어진 데이터에 적용하면 보다 깔끔한 그래프를 얻을 수 있습니다.
기본 그래프 유클리디안 방법과 폴리곤 geom을 설정한 그래프 마치며
ggplot은 분명 유용한 시각화 도구이나, ggplot에 맞는 데이터 형식이 있기 때문에 적절한 사용을 위한 데이터 정제가 없다면 만들기까지 매우 느리고 완성되도 지저분한 그림이 될 가능성이 높습니다. 그렇지만 위 11+@의 그래프들을 잘 확인해보고 실제 데이터로 연습한다면, 대부분의 실무 니즈에 맞는 시각화 능력을 갖출 수 있을 것입니다.
[컨텐츠 코드]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters#============================================================= # ggplot2 # # Author : Junmo Nam # #============================================================= sapply(c('dplyr','ggplot2'),require,character.only = T) #============================================================= # 1. scatter plot #============================================================= #basic iris %>% ggplot(aes(Sepal.Length,Sepal.Width))+ geom_point() #colored by p = iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Species))+ geom_point();p #add line p+geom_line() #different aesthetic for line and point (colored line only) iris %>% ggplot(aes(Sepal.Length,Sepal.Width))+ geom_point()+ geom_line(aes(col = Species)) #color outside of aesthetic iris %>% ggplot(aes(Sepal.Length,Sepal.Width))+ geom_point(col = 'blue')+ geom_line(col = 'red') #smooth line p+geom_smooth() p+geom_smooth(method = 'lm') #smoothline without area p+geom_smooth(se = F) #add text p+geom_text(aes(label = Species)) #============================================================= # 2. Histogram #============================================================= #basic iris %>% ggplot(aes(x = Sepal.Length))+ geom_histogram() #modify bin iris %>% ggplot(aes(x = Sepal.Length))+ geom_histogram(binwidth = .1) #============================================================= # 3. barplot #============================================================= #basic iris %>% ggplot(aes(x=Sepal.Length))+ geom_bar() #colored by iris %>% ggplot(aes(x = Sepal.Length,color = Species))+ geom_bar() #filled by iris %>% ggplot(aes(x = Sepal.Length,fill = Species))+ geom_bar() #barplot with 2 variables iris %>% ggplot(aes(x = Species,y = Sepal.Length,fill = Species))+ geom_bar(stat = 'identity') #fill position iris %>% ggplot(aes(x = Sepal.Length,fill = Species))+ geom_bar(position = 'fill') #dodge position iris %>% ggplot(aes(x = Sepal.Length,fill = Species))+ geom_bar(position = 'dodge') #stack position iris %>% ggplot(aes(x = Sepal.Length,fill = Species))+ geom_bar(position = 'stack') #============================================================= # 4. Densitry plot #============================================================= #basic iris %>% ggplot(aes(x = Sepal.Width))+ geom_density() #remove bottom line iris %>% ggplot(aes(x = Sepal.Width))+ geom_line(stat = 'density') #2d density plot p = iris %>% ggplot(aes(x = Sepal.Width,y = Sepal.Length))+ geom_density2d();p #add colored point on density 2d p + geom_point(aes(color = Species)) #============================================================= # 5. Boxplot #============================================================= #basic iris %>% ggplot(aes(x = Species, y = Sepal.Length))+ geom_boxplot() #colored by iris %>% ggplot(aes(x = Species, y = Sepal.Length,color = Species))+ geom_boxplot() #============================================================= # Theme and labels #============================================================= g = iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Species))+ geom_point();g #bw theme g = g + theme_bw();g #add title and subtitle g = g + ggtitle('Main',subtitle = 'Sub');g #edit xlab and ylab g = g + xlab('This is Xlab') + ylab('This is Ylab');g #Theme : changing style of ggplot #remove axis title g + theme(axis.title = element_blank()) #remove axis tick g + theme(axis.ticks = element_blank()) #remove x axis' text g + theme(axis.text.x = element_blank()) #add bold on plot title g + theme(plot.title = element_text(face = 'bold')) #remove legend g + theme(legend.position = 'none') #============================================================= # coord : Coordinate system #============================================================= #flip x and y g + coord_flip() #polar coordinate g + coord_polar() #cartesian : zooming plot g + coord_cartesian(xlim = c(6,7)) g + coord_cartesian(ylim = c(3,6)) #============================================================= # Faceting #============================================================= #grid g + facet_grid(~Species) #row based g + facet_grid(Species~.) #what if : scale of each group vary df = data.frame(val = c(runif(10,min = 50, max = 200), runif(10,min = 1, max = 20), runif(10,min = 500, max = 2000)), val2 = c(runif(10,min = 3, max = 5), runif(10,min = 10, max = 70), runif(10,min = 0.5, max = 1.2)), g = c(rep('a',10),rep('b',10),rep('c',10))) df %>% ggplot(aes(val,val2,col = g))+ geom_point()+ theme_bw()+ ggtitle('Facet grid','without using different y axis')+ facet_grid(~g) #use same y -> hard to see df %>% ggplot(aes(val,val2,col = g))+ geom_point()+ theme_bw()+ ggtitle('Facet wrap with free scale','using different y axis')+ facet_wrap(~g,scales = 'free') #wrapping y axis #facet grid는 적용되지 않음 #============================================================= # Coloring #============================================================= #continuous coloring iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Petal.Length))+ geom_point() #coloring using palette iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Species))+ geom_point()+ scale_color_manual(values = rainbow(3)) #using other preset palette iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Species))+ geom_point()+ scale_color_brewer(palette = 'Dark2') #gradient : coloring for continuous data iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Petal.Length))+ geom_point()+ scale_color_gradient(low = 'red',high = 'green') #gradient2 : coloring for continuous data + set middle color iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Petal.Length))+ geom_point()+ scale_color_gradient2(low = 'red',high = 'green',mid = 'grey',midpoint = 3) #============================================================= # Shaping dots #============================================================= iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Species))+ geom_point(shape = 0) iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Species))+ geom_point(shape = 15) #shape by aesthetic iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Species,shape = Species))+ geom_point() #============================================================= # size control and alpha #============================================================= #size iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Species,shape = Species))+ geom_point(size = 5) #alpha iris %>% ggplot(aes(Sepal.Length,Sepal.Width,col = Species,shape = Species))+ geom_point(size = 5,alpha = 0.3) #============================================================= # elipse : draw circle #============================================================= g + stat_ellipse() g + stat_ellipse(aes(fill = Species),geom = 'polygon',type = 'euclid',alpha = 0.3) 'R 이모저모' 카테고리의 다른 글
웹 크롤링 기초와 R (0) 2019.07.21 R로 위키 빌보드 차트 가져오기 (0) 2019.06.23 R과 네트워크 분석(2) (0) 2019.05.29 R과 네트워크 분석 (1) (0) 2019.05.12 Shiny : 대시보드 배포하기 (7) 2019.04.24