산점도는 두 변수간의 관계를 파악하는데 굉장히 유용한 시각화 방법입니다.  그리고 산점도 행렬은 다수의 변수들 간의 관계를 한눈에 파악하는데 유용한 시각화 방법이구요.

 

이전 포스팅에서

- ggplot2의 geom_point() 함수를 이용한 한개 변수의 산점도 그리기

- corrplot 패키지를 이용한 상관계수 행렬 그림 그리기

를 소개했었습니다.

 

ggplot2로 두 변수만 가지고 산점도는 유연하게 그릴 수 있는데요, 3개 이상의 변수를 가지고 산점도 행렬을 그리기는 매우 힘이 듭니다. (프로그래밍을 해야 합니다)  따라서 산점도 행렬은 plot() 함수를 써서 한방에 그리는 것이 제일 편하구요,

 

이번 포스팅에서는 Base Graphics 패키지 내에 pairs() 함수를 이용해서 산점도 행렬에 몇가지 사용자 정의 함수를 추가하여 히스토그램도 집어 넣고 상관계수 숫자도 포함시키는 방법을 소개하겠습니다.  산점도 행렬에 많은 추가 정보를 담을 수 있어서 매우 보기에 좋고 유용합니다. 사용자 정의 함수는 pairs() 도움말(help)을 참조하였습니다.

 

 

예제로 사용한 데이터는 뉴욕의 1973년도 공기의 질을 측정한 airquality 데이터셋의 Ozone, Solar.R, Wind, Temp 4개의 변수가 되겠습니다.

 

> str(airquality)
'data.frame':	153 obs. of  6 variables:
 $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
 $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
 $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
 $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...
 $ Month  : int  5 5 5 5 5 5 5 5 5 5 ...
 $ Day    : int  1 2 3 4 5 6 7 8 9 10 ...
> 

> # 1~4번째 변수만 선택

> airquality_1 <- airquality[,c(1:4)]
> str(airquality_1)
'data.frame':	153 obs. of  4 variables:
 $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
 $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
 $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
 $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ... 

 

 

결측값이 있으면 상관계수를 구할 때 NA 값이 나오므로, 결측값 여부 확인하고 결측값이 있는 행은 삭제한 후에 산점도 행렬을 그려보겠습니다.

 

> # 결측값 개수 확인
> sum(is.na(airquality_1$Ozone)) # 37
[1] 37
> sum(is.na(airquality_1$Solar.R)) # 7
[1] 7
> sum(is.na(airquality_1$Wind)) # 0
[1] 0
> sum(is.na(airquality_1$Temp)) # 0
[1] 0
> 
> # 결측값 있는 상태에서 상관계수 계산했을 때
> cor(airquality_1)
        Ozone Solar.R       Wind       Temp
Ozone       1      NA         NA         NA
Solar.R    NA       1         NA         NA
Wind       NA      NA  1.0000000 -0.4579879
Temp       NA      NA -0.4579879  1.0000000
> 
> # 결측값 있는 행 전체 삭제
> airquality_2 <- na.omit(airquality_1)
> str(airquality_2)
'data.frame':	111 obs. of  4 variables:
 $ Ozone  : int  41 36 12 18 23 19 8 16 11 14 ...
 $ Solar.R: int  190 118 149 313 299 99 19 256 290 274 ...
 $ Wind   : num  7.4 8 12.6 11.5 8.6 13.8 20.1 9.7 9.2 10.9 ...
 $ Temp   : int  67 72 74 62 65 59 61 69 66 68 ...
 - attr(*, "na.action")=Class 'omit'  Named int [1:42] 5 6 10 11 25 26 27 32 33 34 ...
  .. ..- attr(*, "names")= chr [1:42] "5" "6" "10" "11" ...
> sum(is.na(airquality_2$Ozone)) # 0
[1] 0
> sum(is.na(airquality_2$Solar.R)) # 0
[1] 0 

 

 

 

산점도 행렬의 대각선에 히스토그램을 추가하는 사용자 정의 함수입니다.  pairs() 도움말(help)에 나와있는 사용자 정의함수 그대로 가져왔습니다.  아래 사용자 정의 함수를 카피해서 사용하시기 바랍니다.

 

## put histograms on the diagonal
panel.hist <- function(x, ...)
{
  usr <- par("usr"); on.exit(par(usr))
  par(usr = c(usr[1:2], 0, 1.5) )
  h <- hist(x, plot = FALSE)
  breaks <- h$breaks; nB <- length(breaks)
  y <- h$counts; y <- y/max(y)
  rect(breaks[-nB], 0, breaks[-1], y, col = "cyan", ...)

 

# source: help(pairs)

 

 

 

다음으로 산점도 행렬의 위쪽에 상관계수 숫자를 집어넣는 사용자 정의 함수입니다.  이 또한 pairs() 도움말(help)에 나와있는 사용자 정의함수 그대로 가져왔습니다.  아래 사용자 정의 함수를 카피해서 사용하시기 바랍니다.

 

## put (absolute) correlations on the upper panels,
## with size proportional to the correlations.
panel.cor <- function(x, y, digits = 2, prefix = "", cex.cor, ...)
{
  usr <- par("usr"); on.exit(par(usr))
  par(usr = c(0, 1, 0, 1))
  r <- abs(cor(x, y))
  txt <- format(c(r, 0.123456789), digits = digits)[1]
  txt <- paste0(prefix, txt)
  if(missing(cex.cor)) cex.cor <- 0.8/strwidth(txt)
  text(0.5, 0.5, txt, cex = cex.cor * r)

 

# source : help(pairs)

 

 

 

다음으로 산점도에 선형 회귀선을 추가하는 사용자 정의 함수입니다.  이는 R Graphics Cookbook (원스턴 챙 지음, 이제원 옮김)을 참조하였습니다.  아래 사용자 정의 함수를 카피해서 사용하시기 바랍니다.

 

## put linear regression line on the scatter plot
panel.lm <- function(x, y, col=par("col"), bg=NA, pch=par("pch"),
                     cex=1, col.smooth="black", ...) {
  points(x, y, pch=pch, col=col, bg=bg, cex=cex)
  abline(stats::lm(y~x), col=col.smooth, ...)

 

 

 

이제 준비가 다 되었습니다.  airquality의 4개 변수 간의 산점도 행렬, 상관계수 숫자, 히스토그램을 하나의 도표로 나타내보겠습니다.

 

> ## 산점도 행렬(scatter-plot matrix), 상관계수(correlation), 히스토그램(histogram) > pairs(airquality_2, + lower.panel = panel.lm, # 아래쪽 산점도에 선형 직선 추가 + upper.panel = panel.cor, # 위쪽에는 상관계수 숫자 (상관계수에 크기 비례) + diag.panel = panel.hist, # 대각선에는 히스토그램 + pch="*", # 점 모양은 * 로 + main = "scatter-plot matrix, correlation coef., histogram" + )

 

 

 

 

 

 

보너스로, pairs() 함수를 사용해서 범주(그룹)별로 점의 색깔을 달리하는 방법도 소개하겠습니다.  이 역시 pairs() 함수 도움말(help)에 있는 R script 를 가져왔습니다.  도움말(help)이 정말 도움이 많이 됩니다. ^^  사용한 데이터는 그 유명한 Iris 데이터가 되겠습니다.

 

> # 범주(그룹)을 색깔로 구분하여 산점도 행렬 그리기
> pairs(iris[1:4], main = "Anderson's Iris Data -- 3 species",
+       pch = 21, bg = c("red", "green3", "blue")[unclass(iris$Species)])

 

 

 * R script source : help(pairs)

 

많은 도움 되셨기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

Posted by R Friend Rfriend

댓글을 달아 주세요

다수의 변수간 상관관계를 파악하려고 할 때, 회귀분석에서 종속변수와 독립변수간 선형관계를 파악하거나 독립변수간 다중공선성을 파악하려고 할 때 사용하는 분석 기법이 상관계수 행렬이며, 시각화 방법이 산점도 행렬과 상관계수 행렬 Plot (correlation matrix plot) 입니다.

 

이전 포스팅에서 ggplot2의 geom_point() 산점도를 다루었으며,

 

다음 포스팅에서는 Base Graphics 패키지의 pairs() 함수를 사용한 산점도 행렬을 소개하였고,

 

이번 포스팅에서는 상관계수 행렬 Plot을 중심으로 해서 corrplot 패키지 사용법을 알아보겠습니다. 

 

 

예제로 사용한 데이터는 뉴욕의 1973년도 공기의 질을 측정한 airquality 데이터셋의 Ozone, Solar.R, Wind, Temp 4개의 변수가 되겠습니다.

 

 

> str(airquality)
'data.frame':	153 obs. of  6 variables:
 $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
 $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
 $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
 $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...
 $ Month  : int  5 5 5 5 5 5 5 5 5 5 ...
 $ Day    : int  1 2 3 4 5 6 7 8 9 10 ...
> 

> # Month, Day는 빼기

> airquality_1 <- airquality[,c(1:4)]
> 
> str(airquality_1)
'data.frame':	153 obs. of  4 variables:
 $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
 $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
 $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
 $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...

 

 

 

 

상관계수 분석을 할 때 결측값이 있으면 NA 값이 나오게 되므로 사전에 결측값 처리하는 것이 필요합니다.  Ozone과 Solar.R이 결측값이 각각 37개, 7개 있다보니 아래처럼 상관계수가 NA가 나왔습니다.

 

> # 결측값 확인
> sum(is.na(airquality_1$Ozone)) # 37
[1] 37
> sum(is.na(airquality_1$Solar.R)) # 7
[1] 7
> sum(is.na(airquality_1$Wind)) # 0
[1] 0
> sum(is.na(airquality_1$Temp)) # 0
[1] 0
> # 결측값 있는 상태에서 상관계수 계산했을 때
> cor(airquality_1)
        Ozone Solar.R       Wind       Temp
Ozone       1      NA         NA         NA
Solar.R    NA       1         NA         NA
Wind       NA      NA  1.0000000 -0.4579879
Temp       NA      NA -0.4579879  1.0000000

 

 

 

 

na.omit() 함수를 사용하여 결측값이 있는 행 전체를 삭제한 후에 상관계수를 구해보면 아래와 같습니다.  corrplot 패키지의 corrplot() 함수는 상관계수 행렬 데이터셋을 가지고 그래프를 그리므로 아래처럼 결측값을 제거한 후의 데이터셋을 가지고 미리 상관계수 행렬을 계산해두어야 합니다.

 

 

> # 결측값 있는 행 전체 삭제
> airquality_2 <- na.omit(airquality_1)
> str(airquality_2)
'data.frame':	111 obs. of  4 variables:
 $ Ozone  : int  41 36 12 18 23 19 8 16 11 14 ...
 $ Solar.R: int  190 118 149 313 299 99 19 256 290 274 ...
 $ Wind   : num  7.4 8 12.6 11.5 8.6 13.8 20.1 9.7 9.2 10.9 ...
 $ Temp   : int  67 72 74 62 65 59 61 69 66 68 ...
 - attr(*, "na.action")=Class 'omit'  Named int [1:42] 5 6 10 11 25 26 27 32 33 34 ...
  .. ..- attr(*, "names")= chr [1:42] "5" "6" "10" "11" ...
> sum(is.na(airquality_2$Ozone)) # 0
[1] 0
> sum(is.na(airquality_2$Solar.R)) # 0
[1] 0
> # 상관계수 계산
> airquality_cor <- cor(airquality_2)
> airquality_cor
             Ozone    Solar.R       Wind       Temp
Ozone    1.0000000  0.3483417 -0.6124966  0.6985414
Solar.R  0.3483417  1.0000000 -0.1271835  0.2940876
Wind    -0.6124966 -0.1271835  1.0000000 -0.4971897
Temp     0.6985414  0.2940876 -0.4971897  1.0000000
 

 

 

 

corrplot 패키지는 별도의 설치 및 호출이 필요한 패키지이므로 아래의 절차를 거칩니다.

 

> install.packages("corrplot")
Installing package into ‘C:/Users/user/Documents/R/win-library/3.2’
(as ‘lib’ is unspecified)
trying URL 'http://cran.rstudio.com/bin/windows/contrib/3.2/corrplot_0.73.zip'
Content type 'application/zip' length 2680505 bytes (2.6 MB)
downloaded 2.6 MB

package ‘corrplot’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
	C:\Users\user\AppData\Local\Temp\Rtmpk1gkRL\downloaded_packages
> library(corrplot) 

 

 

 

산점도 행렬 그림 (scatter matrix plot)을 복습해보자면 아래와 같습니다.

 

> # scatter plot matrix
> plot(airquality_2)

 

 

 

 

 

correlation plot의 method 에는 method = c("circle", "square", "ellipse", "number", "shade", "color", "pie") 등이 있으며, method별로 하나씩 예를 들어보겠습니다.

 

 

> corrplot(airquality_cor, method="circle")
 

 

 

> corrplot(airquality_cor, method="square")

 

 

 

  

> corrplot(airquality_cor, method="ellipse")

 

 

 

 

> corrplot(airquality_cor, method="number")

 

 

 

 

> corrplot(airquality_cor, method="shade")

 

 

 

 

> corrplot(airquality_cor, method="color")

 

 

 

 

> corrplot(airquality_cor, method="pie")

 

 

 

 

마지막으로 mehtod="shade", 상관관계 방향성 제시, 대각선 값 미제시, 상관계수 숫지 검정색으로 해서 추가해서 corrplot을 그려보겠습니다.  order 는 FPC(First Principle Component), hclust(hierarchical clustering), AOE(Angular Order of Engenvectors) 등이 있으며, 정렬 기준을 지정해주면 같은 색깔 끼리 뭉쳐서 보일 수 있도록 정렬을 시켜줘서 보기에, 해석하기에 더 좋게 보여줍니다.

 

> # corrplot
> corrplot(airquality_cor, 
+          method="shade", # 색 입힌 사각형
+          addshade="all", # 상관관계 방향선 제시
+          # shade.col=NA, # 상관관계 방향선 미제시
+          tl.col="red", # 라벨 색 지정
+          tl.srt=30, # 위쪽 라벨 회전 각도
+          diag=FALSE, # 대각선 값 미제시
+          addCoef.col="black", # 상관계수 숫자 색
+          order="FPC" # "FPC": First Principle Component
+                      # "hclust" : hierarchical clustering
+                      # "AOE" : Angular Order of Eigenvectors
+          )

 

 

 

 

많은 도움이 되었기를 바랍니다.

 

-----------

 

(참고) pairs() 함수를 활용한 '산점도 행렬 + 상관계수 행렬 + 히스토그램' 그리기 ☞ http://rfriend.tistory.com/83

 

-----------

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

Posted by R Friend Rfriend

댓글을 달아 주세요

  1. 스윗쥬스 2016.08.04 17:06 신고  댓글주소  수정/삭제  댓글쓰기

    order Option은 3가지가 있습니다.
    직접 테스트를 했는데도, 어떤 기준으로 정렬하는지 파악이 잘 안되네요.
    각 기준이 어떻게 정렬하는지 알려주실 수 있으실까요..?

    1.FPC(First Principle Component)
    2.hclust(hierarchical clustering)
    3.AOE(Angular Order of Engenvectors)

    • R Friend Rfriend 2016.08.04 23:00 신고  댓글주소  수정/삭제

      스윗쥬스님,

      order option별로 설명을 드리자면,

      1)FPC(First Principle Component)

      주성분분석이 데이터의 분산을 최대로 많이 설명할 수 있는 선형조합을 찾는 것이며, 제1 주성분이 데이터의 정보량을 가장 많이 설명을 합니다. 보통 변수들 간에 상관성이 높을 수록 제1 주성분의 설명력가 높습니다. 이론상으로는 제1 주성분에 대한 변수별 기여도를 가지고 ordering을 하면 서로 상관성이 높은 변수들의 순서대로 정렬이 될겁니다.


      ☞ 주성분분석(PCA) 참고 포스팅 :
      http://rfriend.tistory.com/61


      2)hclust(Hierarchical Clustering) :

      계층적 군집화 방법에는 6가지 세부 알고리즘이 있는데요, 기본 원리는 객체 간 유사성(similarity)을 측정해서 유사한 객체끼리 군집이 하나가 될때까지 반복해서 묶어주는 작업을 합니다. 보통은 Euclidean Distance 를 가지고 유사성(정확히는 비유사성, dissimilarity)를 측정해서 군집화를 해줍니다. 앞서 말씀드린 주성분분석 (혹은 요인분석)은 '변수'에 대해서 '상관성'을 가지고 주성분으로 묶어주는 것이라면, 군집분석은 '객체(관측치)'에 대해서 '유사성'을 가지고 군집으로 묶어주는 것입니다.

      따라서 상관관계분석의 ordering 기준으로 계층적군집분석을 쓴다는게 어떤 매카니즘인지를 저는 정확히 이해를 못하겠습니다. ^^; 죄송합니다.

      보통 군집분석 끝내고 나면 군집별로 특성 파악을 위해 변수들과 cross-tabulation 해가면서 profiling을 하는데요, 각 군집별로 특성을 잘 나타낼 수 있는 변수들이 있다면 그 변수들의 상관성이 높다고 해석할 수도 있을 것 같습니다.

      ☞ 응집형 계층적 군집화 - 단일(최단) 연결법 참고 포스팅 :
      http://rfriend.tistory.com/202


      3) AOE(Angular Order of Eigenvectors) :

      1)번에서 주성분분석을 얘기했었는데요, 주성분 구할 때 성분을 분해하면서 고유값(Eigenvalue)과 고유벡터(Eigenvector)를 사용합니다. 첫번째, 두번째, ... 이 순서대로 고유값이 컸다가 작아지구요, 설명력도 첫번째 고유값, 고유벡터가 제일 크고 점점 작아진다고 생각하시면 됩니다. 1)번의 FPC와 대략 같은 맥락의 order 기준이라고 생각하시면 될거 같습니다.

      ☞ 고유값(eigenvalue), 고유벡터(eigenvector) 참고 포스팅 : http://rfriend.tistory.com/181

    • 스윗쥬스 2016.08.05 16:15 신고  댓글주소  수정/삭제

      답변감사합니다 ~
      덕분에 많이 공부했습니다. ^^

  2. 지나가던학생 2016.11.09 20:26  댓글주소  수정/삭제  댓글쓰기

    저 질문이 있는데... 상관계수 구해서 시각화할때 유의성, 즉 p값은 고려하지 않고, 단순 값만 시각화 된것인가요? 만약 저 매트릭스에서 p값이 유의하지 않게 나온 값은 표시되지 않게 할수 있는 방법은 없을까요

    • R Friend Rfriend 2016.11.10 00:26 신고  댓글주소  수정/삭제

      pairs() 함수와 사용자 정의 함수를 사용하면 (두 변수들 간의) 산점도 행렬 + (한 변수의) 히스토그램 + (두 변수들 간의) 상관계수를 한꺼번에 볼 수 있습니다.

      특히 우측 상단의 상관계수의 절대값 크기에 비례해서 숫자 크기가 달라짐에 따라 상관계수절대값이 큰 값만 눈에 잘 띄며, 별로 관계가 없는 값은 숫자 크기가 매우 작게 나타나는 시각화 효과를 제공합니다.

      아래 링크의 포스팅 참고하세요.

      => http://rfriend.tistory.com/83

  3. 오타 2020.12.12 21:32  댓글주소  수정/삭제  댓글쓰기

    맨 밑에서 두번째 줄에 "상관계수 행렬 그리기" 링크라고 쓰였는데,
    링크 가보면 "산점도 행렬 그리기"네요.
    의미는 이해가 가지만 용어는 수정해야 할 것 같아요.

    공교롭게도 이 오타를 그대로 인용한 것인지, 데이터에듀에서 출간한 "ADP 실기 데이터 분석 전문가"에서도 상관행렬 시각화라고 적어놓고, 산점도를 그려놨네요.

    • R Friend Rfriend 2020.12.13 00:14 신고  댓글주소  수정/삭제

      안녕하세요.

      링크에 가보시면 시각화 포스팅 내용에
      "산점도 행렬 + 상관계수 행렬 + 히스토그램 + 선형회귀선"을 모두 아우르는 사용자 정의 함수를 소개하였습니다.

      모두 포괄하는 내용으로 내용 수정하겠습니다.

ggplot2로 막대그래프를 그렸는데 데이터가 양수와 음수로 구분이 되는 경우 그래프의 가독성을 높이기 위해서 양수냐, 음수냐에 따라 색상을 다르게 하고 싶을 때가 있습니다.

 

이번 포스팅에서는 R에 내장되어 있는 airquaility 데이터셋 (뉴욕의 1973년 5월~9월까지의 daily air quality measurements) 에서 5월달 온도(Temp) 만을 가져온 후에, 5월달 daily 온도의 1차 차분 데이터를 만들어서 막대그래프를 그려보도록 하겠습니다.

 

> str(airquality) 'data.frame': 153 obs. of 6 variables: $ Ozone : int 41 36 12 18 NA 28 23 19 8 NA ... $ Solar.R: int 190 118 149 313 NA NA 299 99 19 194 ... $ Wind : num 7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ... $ Temp : int 67 72 74 62 56 66 65 59 61 69 ... $ Month : int 5 5 5 5 5 5 5 5 5 5 ... $ Day : int 1 2 3 4 5 6 7 8 9 10 ... > sum(is.na(airquality$Temp)) [1] 0

>

> # 5월 온도만 선택
> May <- subset(airquality, select = c(Month, Day, Temp), subset = (Month == "5"))

>

 

 

온도의 1차 차분은 diff(변수, lag=차수) 함수를 사용합니다.  아래는 1차 차분을 하였으므로 5월1일은 빼고, 5월2일부터 5월31일까지의 날짜만 가져온 후에, 날짜와 온도 1차 차분한 값을 data frame으로 묶었습니다.  그 후에 ifelse() 함수를 사용해서 온도 1차 차분 값이 0 이상이면 "PLUS", 0 미만이면 "MINUS"라는 구분자 변수를 새로 생성하였습니다. 

 

> # 온도의 1차 차분
> May_Temp_Diff <- diff(May$Temp, lag=1)
> May_Temp_Diff
 [1]   5   2 -12  -6  10  -1  -6   2   8   5  -5  -3   2 -10   6   2  -9  11  -6  -3  14 -12   0  -4   1  -1  10  14  -2
[30]  -3
 
 

> # 5월2일 ~ 5월31일 날짜 변수 > May_Day <- May[c(2:31), c("Day")] > May_Day [1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 >

 

> May_Temp_Diff.df <- data.frame(May_Day, May_Temp_Diff) > May_Temp_Diff.df May_Day May_Temp_Diff 1 2 5 2 3 2 3 4 -12 4 5 -6 5 6 10 6 7 -1 7 8 -6 8 9 2 9 10 8 10 11 5 11 12 -5 12 13 -3 13 14 2 14 15 -10 15 16 6 16 17 2 17 18 -9 18 19 11 19 20 -6 20 21 -3 21 22 14 22 23 -12 23 24 0 24 25 -4 25 26 1 26 27 -1 27 28 10 28 29 14 29 30 -2 30 31 -3 >

> # 온도 차분 plus, minus 여부 구분자 변수 생성

> attach(May_Temp_Diff.df) > May_Temp_Diff.df$plus_minus <- ifelse(May_Temp_Diff >= 0, "PLUS", "MINUS") > May_Temp_Diff.df May_Day May_Temp_Diff plus_minus 1 2 5 PLUS 2 3 2 PLUS 3 4 -12 MINUS 4 5 -6 MINUS 5 6 10 PLUS 6 7 -1 MINUS 7 8 -6 MINUS 8 9 2 PLUS 9 10 8 PLUS 10 11 5 PLUS 11 12 -5 MINUS 12 13 -3 MINUS 13 14 2 PLUS 14 15 -10 MINUS 15 16 6 PLUS 16 17 2 PLUS 17 18 -9 MINUS 18 19 11 PLUS 19 20 -6 MINUS 20 21 -3 MINUS 21 22 14 PLUS 22 23 -12 MINUS 23 24 0 PLUS 24 25 -4 MINUS 25 26 1 PLUS 26 27 -1 MINUS 27 28 10 PLUS 28 29 14 PLUS 29 30 -2 MINUS 30 31 -3 MINUS 

 
> detach(May_Temp_Diff.df)

 

 

ggplot2 패키지는 사용자가 추가로 설치해야 합니다.  intall.packages()함수로 설치하고 library() 함수로 호출해보겠습니다.

 

> install.packages("ggplot2")
> library(ggplot2) 

 

 

 

이제 준비가 다 되었습니다.  1차 차분한 5월달의 온도에 대해서 양수(전날 보다 온도 상승)는 빨간색, 음수(전날보다 온도 하락)는 파란색으로 막대 그래프를 그려보겠습니다.  aes(fill=구분자 변수) 함수를 사용하고, 색깔지정은 scale_fill_manual(values=c(색깔1, 색깔2)) 로 지정해주면 됩니다.

 

> # 양수는 빨간색, 음수는 파란색으로 막대 색 구분
> ggplot(data=May_Temp_Diff.df, aes(x=May_Day, y=May_Temp_Diff, fill=plus_minus)) + 
+   geom_bar(stat="identity", position="identity", colour="white", width=0.2) + # width 막대 폭 좁게
+   scale_fill_manual(values=c("blue", "red"), guide=FALSE) + # guide=F 범례 생략
+   ggtitle("1st order differenced Temp of May")

 

 

 

 

 

 

막대 폭이 너무 가늘어서 보기 싫다면, 막대 폭을 좀더 넓히고 싶다면 geom_bar(width=숫자) 함수를 사용하면 됩니다.

 

> # width 막대 폭 넓게
> ggplot(data=May_Temp_Diff.df, aes(x=May_Day, y=May_Temp_Diff, fill=plus_minus)) + 
+   geom_bar(stat="identity", position="identity", colour="white", width=1) + # width 막대 폭 넓게 
+   scale_fill_manual(values=c("blue", "red"), guide=FALSE) + # guide=F 범례 생략
+   ggtitle("1st order differenced Temp of May")

 

 

 

 

 

다음으로, 위의 그래프에서 보면 ggplot2 가 알아서 x축을 10, 20, 30으로 해서 10일 간격으로 설정해서 그래프를 그렸는데요, 이를 좀더 세분화하고 싶다면 scale_x_continuous(breaks=c(숫자, 숫자...)) 로 지정해주면 됩니다.

 

> # x축 세분화
> ggplot(data=May_Temp_Diff.df, aes(x=May_Day, y=May_Temp_Diff, fill=plus_minus)) + 
+   geom_bar(stat="identity", position="identity", colour="white", width=0.5) + 
+   scale_fill_manual(values=c("blue", "red"), guide=FALSE) + 
+   ggtitle("1st order differenced Temp of May") + 
+   scale_x_continuous(breaks=c(5, 10, 15, 20, 25, 30)) # x축 세분화

 

 

 

 

 

마지막으로, x축과 y축의 라벨 이름이 위에 보면 변수명이 그래도 들어가 있는데요, 좀더 이해하기 쉽도록 xlab(), ylab() 함수를 추가하여 x축, y축 라벨을 변경해보도록 하겠습니다.

 

> # modification of x label, y label
> ggplot(data=May_Temp_Diff.df, aes(x=May_Day, y=May_Temp_Diff, fill=plus_minus)) + 
+   geom_bar(stat="identity", position="identity", colour="white", width=0.5) + 
+   scale_fill_manual(values=c("blue", "red"), guide=FALSE) + 
+   ggtitle("1st order differenced Temp of May") + 
+   scale_x_continuous(breaks=c(5, 10, 15, 20, 25, 30)) + 
+   xlab("Day of May, 1973") + 
+   ylab("Temp difference from previous day")

 

 

 

 

많은 도움 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 

Posted by R Friend Rfriend

댓글을 달아 주세요

DB에 들어있는 데이터를 내렸을 때, 설문조사 데이터를 받았을 때, 원천 거래 데이터를 받았을 때, 혹은 분석 과정 중의 데이터 셋이 분석가가 하고자 하는 통계분석, 데이터마이닝 분석이나 ggplot2 등의 그래프/시각화를 위해 필요한 데이터 구조로 딱 맞아떨어지지 않는 경우가 굉장히 많습니다.

 

이때 필요한 것이 데이터를 분석 목적, 기법에 맞게 자유자재로 변형하여 재구조화하는 일입니다.  

 

엑셀에서 Pilvot Table 생성하는 것이 제일 이해하기 쉬운 예가 될거 같습니다.  세로로 길게 늘어서 있는 원 데이터를 가로와 세로로 틀을 잡아 주고, 가운데에는 value 값에 대해서 개수를 센다든지, 합계를 낸다든지, 평균을 구한다든지 하는 함수를 적용하는 것을 해보셨을 겁니다. 

 

비유를 해보자면, 레고블록으로 높이 세워진 탑을 모양과 색깔별로 레고 블록을 잘 분해한 다음에, 이를 재료로 해서 가로와 세로로 넓게 펴고 높이는 낮추어서 새로운 탑을 만드는 것이라고 생각해보는 것도 이해하는데 도움이 될거 같습니다.

 

이때 데이터 재구조화하는 기준 변수를 무엇으로 하느냐, 가로로 길게 늘어뜨릴 변수는 또 무엇으로 하느냐, 가운데에 값을 볼 때 무슨 함수를 사용해서 값을 계산해서 볼 것이냐에 따라서 다양한 경우의 수가 발생하게 됨에 따라 말로 모든 것을 설명하는 것이 무리가 있습니다.  따라서 아래에 reshape 패키지의 melt(), cast() 함수를 몇 가지 경우의 수에 적용해 보면서 원래 값이 이후에 어떻게 바뀌는지를 유심히 보시고, 필요한 상황에 맞는 R script를 참고하시면 되겠습니다.

 

 

[ reshape 패키지 내 melt()함수, cast() 함수 사용 데이터 재구조화 예시 ]

 

 

 

 

예제로 사용할 데이터는 MASS 패키지에 내장되어 있는 Cars93 데이터 프레임의 차종(Type), 제조국(Origin), 도시 연비(MPG.city), 고속도로 연비(MPG.highway) 4개의 변수를 사용해서 몇가지의 경우의 수를 조합해 가면서 데이터를 녹였다가 (melt) 재구조화 (cast) 를 해보겠습니다.

 

> library(MASS) > str(Cars93) 'data.frame': 93 obs. of 27 variables: $ Manufacturer : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ... $ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ... $ Type : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ... $ Min.Price : num 12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ... $ Price : num 15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ... $ Max.Price : num 18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ... $ MPG.city : int 25 18 20 19 22 22 19 16 19 16 ... $ MPG.highway : int 31 25 26 26 30 31 28 25 27 25 ... $ AirBags : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ... $ DriveTrain : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ... $ Cylinders : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ... $ EngineSize : num 1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ... $ Horsepower : int 140 200 172 172 208 110 170 180 170 200 ... $ RPM : int 6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ... $ Rev.per.mile : int 2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ... $ Man.trans.avail : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ... $ Fuel.tank.capacity: num 13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ... $ Passengers : int 5 5 5 6 4 6 6 6 5 6 ... $ Length : int 177 195 180 193 186 189 200 216 198 206 ... $ Wheelbase : int 102 115 102 106 109 105 111 116 108 114 ... $ Width : int 68 71 67 70 69 69 74 78 73 73 ... $ Turn.circle : int 37 38 37 37 39 41 42 45 41 43 ... $ Rear.seat.room : num 26.5 30 28 31 27 28 30.5 30.5 26.5 35 ... $ Luggage.room : int 11 15 14 17 13 16 17 21 14 18 ... $ Weight : int 2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ... $ Origin : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ... $ Make : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...

 

 

데이터 양이 너무 많으면 melt(), cast()를 적용했을 때 데이터 구조가 변화하는 모양을 보기가 쉽지 않기 때문에 차종(Type) 중에서 개수가 적은 "Compact"와 "Van" 만 선별해서 예제로 사용하겠습니다.

 

 

> table(Cars93$Type)

Compact   Large Midsize   Small  Sporty     Van 
     16      11      22      21      14       9 
> 
> Cars93_sample <- subset(Cars93, 
+                         select = c(Type, Origin, MPG.city, MPG.highway), 
+                         subset = (Type %in% c("Compact", "Van"))) 
> 
> Cars93_sample
      Type  Origin MPG.city MPG.highway
3  Compact non-USA       20          26
12 Compact     USA       25          36
13 Compact     USA       25          34
16     Van     USA       18          23
17     Van     USA       15          20
21 Compact     USA       23          28
25 Compact     USA       22          27
26     Van     USA       17          21
33 Compact     USA       22          27
36     Van     USA       15          20
43 Compact non-USA       24          31
55 Compact non-USA       26          34
56     Van non-USA       18          24
58 Compact non-USA       20          29
65 Compact non-USA       24          30
66     Van non-USA       17          23
68 Compact     USA       24          31
70     Van     USA       18          23
74 Compact     USA       23          31
78 Compact non-USA       20          26
82 Compact non-USA       23          30
87     Van non-USA       18          22
89     Van non-USA       17          21
90 Compact non-USA       21          30
92 Compact non-USA       21          28
> dim(Cars93_sample)
[1] 25  4

 

 



  reshape 패키지의 melt(), cast() 함수를 이용한 데이터 재구조화


 

R의 reshape 패키지는 별도의 설치가 필요합니다. 

 

# reshape package installation, library
> install.packages("reshape")

> library(reshape)

 

 

 

이제 melt(data, id.vars, measure.vars) 함수를 사용해서 기존 데이터셋을 녹여보도록 하겠습니다. 

 

> # melt()
> Cars93_sample_melt <- melt(data = Cars93_sample, 
+                            id.vars = c("Type", "Origin"), 
+                            measure.vars = c("MPG.city", "MPG.highway"))
> 
> Cars93_sample_melt
      Type  Origin    variable value
1  Compact non-USA    MPG.city    20
2  Compact     USA    MPG.city    25
3  Compact     USA    MPG.city    25
4      Van     USA    MPG.city    18
5      Van     USA    MPG.city    15
6  Compact     USA    MPG.city    23
7  Compact     USA    MPG.city    22
8      Van     USA    MPG.city    17
9  Compact     USA    MPG.city    22
10     Van     USA    MPG.city    15
11 Compact non-USA    MPG.city    24
12 Compact non-USA    MPG.city    26
13     Van non-USA    MPG.city    18
14 Compact non-USA    MPG.city    20
15 Compact non-USA    MPG.city    24
16     Van non-USA    MPG.city    17
17 Compact     USA    MPG.city    24
18     Van     USA    MPG.city    18
19 Compact     USA    MPG.city    23
20 Compact non-USA    MPG.city    20
21 Compact non-USA    MPG.city    23
22     Van non-USA    MPG.city    18
23     Van non-USA    MPG.city    17
24 Compact non-USA    MPG.city    21
25 Compact non-USA    MPG.city    21
26 Compact non-USA MPG.highway    26
27 Compact     USA MPG.highway    36
28 Compact     USA MPG.highway    34
29     Van     USA MPG.highway    23
30     Van     USA MPG.highway    20
31 Compact     USA MPG.highway    28
32 Compact     USA MPG.highway    27
33     Van     USA MPG.highway    21
34 Compact     USA MPG.highway    27
35     Van     USA MPG.highway    20
36 Compact non-USA MPG.highway    31
37 Compact non-USA MPG.highway    34
38     Van non-USA MPG.highway    24
39 Compact non-USA MPG.highway    29
40 Compact non-USA MPG.highway    30
41     Van non-USA MPG.highway    23
42 Compact     USA MPG.highway    31
43     Van     USA MPG.highway    23
44 Compact     USA MPG.highway    31
45 Compact non-USA MPG.highway    26
46 Compact non-USA MPG.highway    30
47     Van non-USA MPG.highway    22
48     Van non-USA MPG.highway    21
49 Compact non-USA MPG.highway    30
50 Compact non-USA MPG.highway    28
> dim(Cars93_sample_melt)
[1] 50  4

 

 

 

 

이렇게 melt()함수를 사용해 녹인 데이터를 cast()함수를 사용해서 재구조화 해보겠습니다.  세로와 가로에 무슨 변수를 넣을지가 결정되었다면 아래의 예제를 참고해서 원하는 구조에 맞게 R script를 작성하시면 되겠습니다. (말로 설명하기가 쉽지가 않습니다 ^^;) function 란에는 R에서 사용할 수 있는 통계량 함수를 사용하면 되며, 이번 예제에서는 평균(mean) 함수를 사용하였습니다. 

 

> # cast()
> options(digits=3) # 소숫점 너무 밑에 까지 나오지 않도록 설정
> 
> # 한개의 id.var 기준(세로) & variable(가로) 조합의 value 값에 mean 함수 적용
> cast(data = Cars93_sample_melt, Type ~ variable, fun = mean)
     Type MPG.city MPG.highway
1 Compact     22.7        29.9
2     Van     17.0        21.9 
> cast(data = Cars93_sample_melt, Origin ~ variable, fun = mean)
   Origin MPG.city MPG.highway
1     USA     20.6        26.8
2 non-USA     20.7        27.2
> 

 > # 두개의 id.var 기준(세로) & variable(가로) 조합의 value 값에 mean 함수 적용

> cast(data = Cars93_sample_melt, Type + Origin ~ variable, fun = mean)
     Type  Origin MPG.city MPG.highway
1 Compact     USA     23.4        30.6
2 Compact non-USA     22.1        29.3
3     Van     USA     16.6        21.4
4     Van non-USA     17.5        22.5
> 

 > # 한개의 id.var 기준(세로) & 다른 id.var + variable (가로) 조합의 value 값에 mean 함수 적용

> cast(data = Cars93_sample_melt, Type ~ Origin + variable, fun = mean)
     Type USA_MPG.city USA_MPG.highway non-USA_MPG.city non-USA_MPG.highway
1 Compact         23.4            30.6             22.1                29.3
2     Van         16.6            21.4             17.5                22.5
> cast(data = Cars93_sample_melt, Origin ~ Type + variable, fun = mean)
   Origin Compact_MPG.city Compact_MPG.highway Van_MPG.city Van_MPG.highway
1     USA             23.4                30.6         16.6            21.4
2 non-USA             22.1                29.3         17.5            22.5
> 

 > # 한개의 id.var + variable (세로) & 다른 id.var (가로) 조합의 value 값에 mean 함수 적용

> cast(data = Cars93_sample_melt, Type + variable ~ Origin, fun = mean)
     Type    variable  USA non-USA
1 Compact    MPG.city 23.4    22.1
2 Compact MPG.highway 30.6    29.3
3     Van    MPG.city 16.6    17.5
4     Van MPG.highway 21.4    22.5

 > cast(data = Cars93_sample_melt, Origin + variable ~ Type, fun = mean)

   Origin    variable Compact  Van
1     USA    MPG.city    23.4 16.6
2     USA MPG.highway    30.6 21.4
3 non-USA    MPG.city    22.1 17.5
4 non-USA MPG.highway    29.3 22.5

 

 

 


  reshape2 패키지의 acast() 함수를 이용한 데이터 재구조화



> library(gcookbook)

> data(cabbage_exp)

> cabbage_exp

  Cultivar Date Weight        sd  n         se

1      c39  d16   3.18 0.9566144 10 0.30250803

2      c39  d20   2.80 0.2788867 10 0.08819171

3      c39  d21   2.74 0.9834181 10 0.31098410

4      c52  d16   2.26 0.4452215 10 0.14079141

5      c52  d20   3.11 0.7908505 10 0.25008887

6      c52  d21   1.47 0.2110819 10 0.06674995

> #install.packages("reshape2")

> library(reshape2)

> cabbage_exp_cast <- acast(data = cabbage_exp

+                           , Cultivar ~ Date

+                           , value.var = 'Weight'

+                           , fun.aggregate = mean

+                           #, fill = 0 # if you want to fill the missing value with '0'

+                           , drop = TRUE)

> cabbage_exp_cast

     d16  d20  d21

c39 3.18 2.80 2.74

c52 2.26 3.11 1.47

 




  tidyverse 패키지의 spread() 함수를 이용한 데이터 재구조화



> # load data

> library(gcookbook)

> data(cabbage_exp)

> cabbage_exp

  Cultivar Date Weight        sd  n         se

1      c39  d16   3.18 0.9566144 10 0.30250803

2      c39  d20   2.80 0.2788867 10 0.08819171

3      c39  d21   2.74 0.9834181 10 0.31098410

4      c52  d16   2.26 0.4452215 10 0.14079141

5      c52  d20   3.11 0.7908505 10 0.25008887

6      c52  d21   1.47 0.2110819 10 0.06674995

> # reshape data using tidyverse

> library(tidyverse)

> # melt first (long format)

> cabbage_exp_melt <- cabbage_exp %>% 

+   group_by(Cultivar, Date) %>%

+   summarise(Weight = mean(Weight))

> # cast second (wide format)

> cabbage_exp_cast <- spread(cabbage_exp_melt, key = c("Date"), value = "Weight", fill = 0)

> print(cabbage_exp_cast)

# A tibble: 2 x 4

# Groups:   Cultivar [2]

  Cultivar   d16   d20   d21

  <fct>    <dbl> <dbl> <dbl>

1 c39       3.18  2.8   2.74

2 c52       2.26  3.11  1.47

 


 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

Posted by R Friend Rfriend

댓글을 달아 주세요

  1. 빵먹어 2016.05.13 18:05  댓글주소  수정/삭제  댓글쓰기

    공부하는 데 많은 도움이 되어 제 블로그에 공유하였습니다.
    내용 변환없이, 그리고 원작자 출처 명확히 밝혔습니다.
    좋은 글 감사합니다.

    • R Friend Rfriend 2016.05.13 19:12 신고  댓글주소  수정/삭제

      블로그 포스팅의 부분 인용 & 출처 기입은 괜찮구요,

      그게 아니라 블로그 포스팅 통 스크랩일 경우는
      (1) 제목 & 링크,
      or (2) (개인학습 용도로) 비공개 전환,
      or (3) (개인학습 용도로) 즐겨찾기 추가를 해서
      이용하시기 바랍니다.

      네이버의 '스크랩' 같이 원본 그대로 옮겨 포스팅하는 경우(비록 출처를 밝혔다 하더라도) 외국 블로깅에서는 있을 수 없는 일이예요.

      제가 블로깅 하는 원칙이기도 하구요, 블로거들이 서로 지키는 예절이기도 합니다.

      이해해 주시기 바랍니다.

  2. AshtrayK 2016.09.28 09:56 신고  댓글주소  수정/삭제  댓글쓰기

    질문있어요~ 이글에 맞는지는 모르겠지만..
    칼럼1(id)에는 725284 , 칼럼2(keyword_count)에는 대출:4^출금:3^이자:2
    이런식의 데이터가 담겨 있습니다.
    칼럼2의 데이터는 id별 담겨있는키워드와 그 횟수인데요. 대출이라는 키워드가 4번 출금이라는 키워드 3번 나타나고 있다는 뜻입니다

  3. AshtrayK 2016.09.28 09:58 신고  댓글주소  수정/삭제  댓글쓰기

    이걸
    id, 키워드, 카운트 3개의 칼럼으로 해서 행하나에 키워드와 카운트를 한개씩만 들어가게하려면 어떻게해야할까요?

  4. AshtrayK 2016.09.28 09:59 신고  댓글주소  수정/삭제  댓글쓰기

    예를들어
    3301 대출 4
    3301 출금 3
    3301 이자 2

  5. AshtrayK 2016.09.28 10:00 신고  댓글주소  수정/삭제  댓글쓰기

    for문으로 만들어보긴했는데 원본의 1400여개 행을 작업해서 7만개 행으로 쪼개는데 20분이 드네요.. 너무오래걸려서 혹시 적절한 다른 방법이 있을까해서 질문드립니다~

    • R Friend Rfriend 2016.09.28 10:10 신고  댓글주소  수정/삭제

      (1) 2번째 칼럼 쪼개기
      (2) subset()으로 id, column(대출, 출금, 이자 별로 각 각), value(각 column 별 회수) 로 쌍으로 해서 칼럼 개수만큼 실행 (이 예제는 대출, 출금, 이자 3번)
      (3) rbind로 3개 데이터셋 합치기

      이렇게 한번 해보시지요.

  6. AshtrayK 2016.09.28 10:20 신고  댓글주소  수정/삭제  댓글쓰기

    (2)subset부분이 이해가 잘 안가는데요.. 음.. 이 데이터셋의 뜻은 각 콜당 핵심키워드의 빈도를 나타내는 거구요. 키워드의 종류 수는 제한이 없습니다. 즉 1행 2열에 들어가있는 키워드 종류가 대출 출금 이자 3가지라면 2행 2열에는 15가지일 수도 있습니다. 실제데이터에서 첫번째꺼는 46개구요.
    즉 2열의 한칸마다 데이터가 너무 길어서 보기 좋게하려고 키워드를 쪼개서 세로로 형태를 바꾸려고 하는거구요 ㅠㅠ

  7. 건영 2016.11.17 17:48  댓글주소  수정/삭제  댓글쓰기

    혼자 공부중인데 많은 도움받고 갑니당~

  8. Park_army 2020.06.18 20:42  댓글주소  수정/삭제  댓글쓰기

    안녕하세요!! 혹시 아까 오후에 질문 남겼었는데 확인해주시면 정말 감사하겠습니다ㅠㅠ

거래 원 데이터 (transaction raw data)를 받으면 분석 용도에 맞게 데이터 전처리를 할 때 보통 하는 일이 특정 기준 (가령, 고객 ID, 상품 ID, 채널 ID 등)에 대해 데이터를 집계(합계, 평균, 분산 등의 함수를 적용)하는 작업을 하게 됩니다. 

 

R에는 aggregate() 라는 함수가 있습니다만, 기존에 SQL에 익숙한 분석가라면 R 에서 SQL 문을 사용할 수 있게 해주는 sqldf package를 사용하면 쉽고 빠르게 집계를 할 수 있겠습니다. 

 

(단, sqldf 가 performance 이슈가 있으니 데이터 사이즈가 크다면, 그리고 데이터 처리 속도가 중요한 경우라면 sqldf 는 부적할 수도 있다는 점은 고려하셔야 겠습니다.)

 

R sqldf package 소개자료에 보면

  - Perform SQL Selects on R Data Frames
  - Manipulate R data frames using SQL

이라고 되어 있습니다.

 

 

 

 

그럼, sqldf package의 여러 기능, 함수 중에서 데이터 집계 관련한 함수만 몇 가지 선별하여서 소개하도록 하겠습니다.

 

실습에 사용할 데이터는 MASS 패키지에 내장된 Cars93 데이터 프레임의 자동차 유형(Type), 도시 연비(MPG.city), 고속도로 연비(MPG.highway) 를 사용하겠습니다.

 

> library(MASS)
> str(Cars93)
'data.frame':	93 obs. of  27 variables:
 $ Manufacturer      : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
 $ Model             : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
 $ Type              : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
 $ Min.Price         : num  12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
 $ Price             : num  15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
 $ Max.Price         : num  18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
 $ MPG.city          : int  25 18 20 19 22 22 19 16 19 16 ...
 $ MPG.highway       : int  31 25 26 26 30 31 28 25 27 25 ...
 $ AirBags           : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
 $ DriveTrain        : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
 $ Cylinders         : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
 $ EngineSize        : num  1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
 $ Horsepower        : int  140 200 172 172 208 110 170 180 170 200 ...
 $ RPM               : int  6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
 $ Rev.per.mile      : int  2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
 $ Man.trans.avail   : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
 $ Fuel.tank.capacity: num  13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
 $ Passengers        : int  5 5 5 6 4 6 6 6 5 6 ...
 $ Length            : int  177 195 180 193 186 189 200 216 198 206 ...
 $ Wheelbase         : int  102 115 102 106 109 105 111 116 108 114 ...
 $ Width             : int  68 71 67 70 69 69 74 78 73 73 ...
 $ Turn.circle       : int  37 38 37 37 39 41 42 45 41 43 ...
 $ Rear.seat.room    : num  26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
 $ Luggage.room      : int  11 15 14 17 13 16 17 21 14 18 ...
 $ Weight            : int  2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
 $ Origin            : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
 $ Make              : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...

 

 

 

R의 aggregate() 함수로 차종(Type)별 도시 연비(MPG.city)와 고속도로 연비(MPG.highway)의 평균을 구해보겠습니다. 

 

> # aggregate

> R_aggregate_mean <- aggregate(Cars93[,c(7,8)], + by = list(Car_Type = Cars93$Type), # list + FUN = mean, # function + na.rm = TRUE)

> > R_aggregate_mean Car_Type MPG.city MPG.highway 1 Compact 22.68750 29.87500 2 Large 18.36364 26.72727 3 Midsize 19.54545 26.72727 4 Small 29.85714 35.47619 5 Sporty 21.78571 28.78571 6 Van 17.00000 21.88889 

 

 

 

 

이번에는 install.packages()함수와 library()함수를 사용하여 sqldf Package 를 설치하고 호출한 후에, sqldf 패키지를 사용하여 위와 같이 차종(Type)별 도시 연비(MPG.city)와 고속도로 연비(MPG.highway)의 평균을 구해보겠습니다.

 

> install.packages("sqldf")
Installing package into ‘C:/Users/user/Documents/R/win-library/3.2’
(as ‘lib’ is unspecified)
trying URL 'http://cran.rstudio.com/bin/windows/contrib/3.2/sqldf_0.4-10.zip'
Content type 'application/zip' length 71825 bytes (70 KB)
downloaded 70 KB

package ‘sqldf’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
	C:\Users\user\AppData\Local\Temp\Rtmp4i7Dhq\downloaded_packages
> library(sqldf)
필요한 패키지를 로딩중입니다: gsubfn
필요한 패키지를 로딩중입니다: proto
필요한 패키지를 로딩중입니다: RSQLite
필요한 패키지를 로딩중입니다: DBI
Warning messages:
1: 패키지 ‘sqldf’는 R 버전 3.2.2에서 작성되었습니다 
2: 패키지 ‘gsubfn’는 R 버전 3.2.2에서 작성되었습니다 
3: 패키지 ‘RSQLite’는 R 버전 3.2.2에서 작성되었습니다 
4: 패키지 ‘DBI’는 R 버전 3.2.2에서 작성되었습니다 

 

> R_sqldf_1 <- sqldf('
+                  select "Type" as "Car_Type", 
+                  avg("MPG.city") as "mean_MPG.city", 
+                  avg("MPG.highway") as "mean_MPG.highway"  
+                  from Cars93 
+                  group by Type
+                  order by Type
+                  ')
> R_sqldf_1
  Car_Type mean_MPG.city mean_MPG.highway
1  Compact      22.68750         29.87500
2    Large      18.36364         26.72727
3  Midsize      19.54545         26.72727
4    Small      29.85714         35.47619
5   Sporty      21.78571         28.78571
6      Van      17.00000         21.88889

 

 

R의 aggregate()함수로 만든 평균과 sqldf로 만든 평균 데이터 셋을 차종(Type) 을 key로 항 merge 한 후에 두 값들이 서로 같은지 한번 점검해보겠습니다.

 

> # 두개 데이터 셋 Merge, 동일 여부 check
> Type_mean <- merge(R_aggregate_mean, R_sqldf_1, by = 'Car_Type')
> Type_mean <- transform(Type_mean, 
+                        gap_MPG.city = MPG.city - mean_MPG.city, 
+                        gap_MPG.highway = MPG.highway - mean_MPG.highway)
> 
> Type_mean
  Car_Type MPG.city MPG.highway mean_MPG.city mean_MPG.highway gap_MPG.city gap_MPG.highway
1  Compact 22.68750    29.87500      22.68750         29.87500            0               0
2    Large 18.36364    26.72727      18.36364         26.72727            0               0
3  Midsize 19.54545    26.72727      19.54545         26.72727            0               0
4    Small 29.85714    35.47619      29.85714         35.47619            0               0
5   Sporty 21.78571    28.78571      21.78571         28.78571            0               0
6      Van 17.00000    21.88889      17.00000         21.88889            0               0

 

얼핏 보면 R의 aggregate() 함수와 sqldf 가 서로 큰 차이가 없거나 혹은 aggregate()함수가 더 편하다고 느낄 수도 있겠습니다.  그런데, 아래의 경우처럼 다수의 함수들(count, sum, avg, variance, stdev, min, max 등)을 그룹 변수에 대해서 구분해서 집계를 할 경우에는, 그리고 SQL에 익숙한 사용자라면 sqldf 패키지를 사용하는게 편할 수 있을 것입니다 

 

 

> # SQL의 aggregation 함수 사용하기
> R_sqldf_2 <- sqldf('
+                    select "Type" as "Car_Type", 
+                    count("MPG.city") as "count_MPG.city", 
+                    sum("MPG.city") as "sum_MPG.city", 
+                    
+                    avg("MPG.city") as "mean_MPG.city", 
+                    variance("MPG.city") as "variance_MPG.city", 
+                    stdev("MPG.city") as "stdev_MPG.city", 
+                    
+                    min("MPG.city") as "min_MPG.city", 
+ 
+                    max("MPG.city") as "max_MPG.city"
+                    
+                    from Cars93 
+                    group by Type
+                    order by Type desc
+                    ')
> 
> # count :  행의 개수
> # sum : 합계
> # avg : 평균
> # var : 분산
> # stddev : 표준편차
> # min : 최소값
> # max : 최대값
> # order by xx desc : 내림차순 정렬
> 
> R_sqldf_2
  Car_Type count_MPG.city sum_MPG.city mean_MPG.city variance_MPG.city stdev_MPG.city min_MPG.city max_MPG.city
1      Van              9          153      17.00000          1.500000       1.224745           15           18
2   Sporty             14          305      21.78571         15.258242       3.906180           17           30
3    Small             21          627      29.85714         37.328571       6.109711           22           46
4  Midsize             22          430      19.54545          3.593074       1.895540           16           23
5    Large             11          202      18.36364          2.254545       1.501514           16           20
6  Compact             16          363      22.68750          3.695833       1.922455           20           26

 

 

변수명을 SQL 문 내에서 바로 부여하는 것도 편리합니다.  그리고 SQL에 능숙한 분석가라면 subquery를 사용해서 한방에 query를 다 돌려서 원하는 데이터셋을 만들어낼 수도 있겠습니다.  (단, sqldf는 속도는 희생될 수 있음)

 

 

그렇다고 sqldf가 데이터 집계를 하는데 있어 모든 통계량을 다 한번에 할 수 있는것은 아닙니다.  R에서는 아래 처럼 median, quantile 을 1줄만에 처리할 수 있는 반면에, 이것과 동일한 결과를 얻으려면 SQL로는 참 어렵습니다.

 

> # R로 median, quantile 지정해서 구하기
> R_aggregate_median <- aggregate(Cars93[,c(7,8)], by = list(Car_Type = Cars93$Type), FUN = median)
> R_aggregate_median
  Car_Type MPG.city MPG.highway
1  Compact     23.0        30.0
2    Large     19.0        26.0
3  Midsize     19.0        26.5
4    Small     29.0        33.0
5   Sporty     22.5        28.5
6      Van     17.0        22.0
> 
> quantile_MPG.city <- quantile(Cars93[,c("MPG.city")], c(0, .01, .05, .1, .25, .5, .75, .9, .95, .99, 1))
> quantile_MPG.city
   0%    1%    5%   10%   25%   50%   75%   90%   95%   99%  100% 
15.00 15.00 16.60 17.00 18.00 21.00 25.00 29.00 31.40 42.32 46.00

 

sqldf 가 편하다고 했다가, 그냥 R 함수가 편하다가 했다가 오락가락 하는 것처럼 보일 수도 있겠는데요, 위의 예제를 보시고 데이터 전처리, 분석의 목적, 상황에 맞게 sqldf와 aggregate() 함수, R 함수를 선별해서 사용하시면 되겠습니다.

 

 

{dplyr} package의 summarise(n = n()), tally(), count() 함수를 사용한 집계 방법은 http://rfriend.tistory.com/240  포스팅을 참고하세요.

 

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

Posted by R Friend Rfriend

댓글을 달아 주세요

  1. AshtrayK 2016.10.13 11:53 신고  댓글주소  수정/삭제  댓글쓰기

    sqldf("select avg(Sepal_Length) from iris where Species='setosa' ")가 안먹힙니다.. Sepal.Length로 써도 마찬가지구요
    iris데이터에서 Species컬럼만 인식되고 나머지는 이것처럼 다 컬럼을 못찾는다고 에러나는데 왜 그러는지 모르겠습니다~

시간의 흐름에 따른 그룹/집단 별 관측값 혹은 비율의 변화를 누적해서 볼 수 있는 그래프가 누적 영역 그래프 (Stacked Area Plot) 입니다.   아마 엑셀에서 많이 보았을 법한 그래프 일것이라고 생각합니다.

 

2007년부터 2014년까지의 한국 수/출입 무역량 (단위: 1 B$) 데이터를 가지고 수출과 수입으로 구분하여서 ggplot2의 geom_area() 함수를 사용하여 누적 영역 그래프(Stacked Area Plot) 를 그려보도록 하겠습니다.

 

아래는 2007년부터 2014년까지의 한국 수/출입 무역량 (단위: 1 B$) 데이터를 링크해두었습니다.

(☞ 한국 수/출입 무역량 데이터 다운로드  trade_stat_07_14.csv)

* 출처 : 국가무역통계 KOSIS, http://kosis.kr/statisticsList/statisticsList_01List.jsp?vwcd=MT_ZTITLE&parmTabId=M_01_01#SubCont)

 

그래프를 그리기에 딱 맞는 형식이 아니므로 csv 데이터를 불러들인 다음에

 -> sqldf 패키지를 활용해 Year 단위로 수출입 실적을 집계

 -> 이때 단위를 1000$ -> 1B$ 로 바꿔주기 위해 1,000,000 으로 나눠줌

을써 데이터를 필요에 맞게 집계해보겠습니다.

 

> trade_stat <- read.csv("C:/Users/user/Documents/R/trade_stat_07_14.csv", # 경로 설정 + header = TRUE) > > > trade_stat <- transform(trade_stat, Year = substr(Time, 1, 4)) > > sapply(trade_stat, class) Time export_amt import_amt Year "numeric" "integer" "integer" "factor" > > library(sqldf) > # 한국 수/출입 무역금액, 단위: 1B$ > trade_stat_Year <- sqldf('select Year, + sum(export_amt)/100000 as exp_amt_Year, + sum(import_amt)/100000 as imp_amt_Year + from trade_stat + group by Year + order by Year + ') > trade_stat_Year Year exp_amt_Year imp_amt_Year 1 2007 3714 3568 2 2008 4220 4352 3 2009 3635 3230 4 2010 4663 4252 5 2011 5552 5244 6 2012 5478 5195 7 2013 5596 5155 8 2014 5726 5255

 

 

 

여기까지 했는데도 누적 영역 그래프를 그리기에 딱 맞는 데이터 형태가 아니라서 reshape 패키지의 melt() 함수를 사용하여 데이터를 현재의 가로로 늘어져있는 exp_amt_Year, imp_amt_Year 변수를 -> 세로로 세워서 데이터 구조를 변경해보겠습니다.

 

그 다음에 variable -> trade_cd (수입, 수출 구분 코드), value -> amount_B (무역금액, 단위 : 1B$) 로 변수명을 변경하였습니다.

 

> # 데이터 구조 녹이기(melt) - 세로로 세우기
> library(reshape)
> trade_stat_Year_melt <- melt(trade_stat_Year, idvars = c("Year"))
Using Year as id variables
> trade_stat_Year_melt
   Year     variable value
1  2007 exp_amt_Year  3714
2  2008 exp_amt_Year  4220
3  2009 exp_amt_Year  3635
4  2010 exp_amt_Year  4663
5  2011 exp_amt_Year  5552
6  2012 exp_amt_Year  5478
7  2013 exp_amt_Year  5596
8  2014 exp_amt_Year  5726
9  2007 imp_amt_Year  3568
10 2008 imp_amt_Year  4352
11 2009 imp_amt_Year  3230
12 2010 imp_amt_Year  4252
13 2011 imp_amt_Year  5244
14 2012 imp_amt_Year  5195
15 2013 imp_amt_Year  5155
16 2014 imp_amt_Year  5255
> 
> # 변수명 변경
> trade_stat_Year_melt <- rename(trade_stat_Year_melt, c(variable="trade_cd", value="amount_B"))
> trade_stat_Year_melt
   Year     trade_cd amount_B
1  2007 exp_amt_Year     3714
2  2008 exp_amt_Year     4220
3  2009 exp_amt_Year     3635
4  2010 exp_amt_Year     4663
5  2011 exp_amt_Year     5552
6  2012 exp_amt_Year     5478
7  2013 exp_amt_Year     5596
8  2014 exp_amt_Year     5726
9  2007 imp_amt_Year     3568
10 2008 imp_amt_Year     4352
11 2009 imp_amt_Year     3230
12 2010 imp_amt_Year     4252
13 2011 imp_amt_Year     5244
14 2012 imp_amt_Year     5195
15 2013 imp_amt_Year     5155
16 2014 imp_amt_Year     5255

 

 

 

이제 드디어 누적 영역 그래프를 그릴 데이터 셋 준비가 다 되었군요.  ggplot2의 geom_area() 함수를 사용하여 우선 값 기준으로 그리고, 다음으로 비율 기준으로도 그려보겠습니다.

 

geom_area(colour=NA)로 하고 geom_line(position="stack")으로 해서 양 옆에 선은 트여주고, 영역 간 경계선은 그려주었습니다.

 

> # 누적 영역 그래프 그리기
> ggplot(trade_stat_Year_melt, aes(x=Year, y=amount_B, fill=trade_cd, group=trade_cd)) +
+   geom_area(colour=NA, alpha=0.5) + # alpha 투명도
+   scale_fill_brewer(palette="Blues") +
+   geom_line(position="stack", size=0.3) + 
+   ggtitle("Stacked Area Plot of Trade (Import, Export) from 2007 to 2014")
ymax not defined: adjusting position using y instead
 

 

 

 

aes(arder=desc()) 를 사용하여 위의 영역 구분 그룹의 순서를 바꿀 수도 있습니다.  위의 예제에서는 exp_amt_Year (수출액)이 아래에 위치했습니다만, 아래 예제에서는 exp_amt_Year(수출액)이 위로 위치가 바뀌었음을 알 수 있습니다.

 

> # 누적 영역 순서 바꾸기
> library(plyr) # desc() 함수 사용 위해 필요
> ggplot(trade_stat_Year_melt, aes(x=Year, y=amount_B, fill=trade_cd, group=trade_cd, 
+                                  order=desc(trade_cd))) + # 누적 영역 순서 내림차순 정렬
+   geom_area(colour=NA, alpha=0.5) + # alpha 투명도
+   scale_fill_brewer(palette="Blues") +
+   geom_line(position="stack", size=0.3) + 
+   ggtitle("Stacked Area Plot of Trade (Import, Export) from 2007 to 2014")
ymax not defined: adjusting position using y instead

 

 

 

 

 

 


 

 

이번에는 비율 기준으로 해서 누적 영역 그래프를 그려보겠습니다.  이를 위해서는 데이터셋에서 Year 별로 비율을 계산해주어야 합니다.  데이터 프레임에서 사칙연산을 써가면서 transform() 함수로 step-by-step 해나갈 수도 있는데요, plyr패키지의 ddply() 함수를 사용하면 놀랍도록 간편하게 원하는 비율 값을 구할 수 있습니다.

 

> #-----
> # 비율 누적 영역 그래프 그리기
> # 비율 계산하기
> library(plyr)
> 
> trade_stat_Year_melt_prop <- ddply(trade_stat_Year_melt, 
+                                    "Year", transform, 
+                                    trade_prop = round(100*amount_B/sum(amount_B),1))
> 
> trade_stat_Year_melt_prop
   Year     trade_cd amount_B trade_prop
1  2007 exp_amt_Year     3714       51.0
2  2007 imp_amt_Year     3568       49.0
3  2008 exp_amt_Year     4220       49.2
4  2008 imp_amt_Year     4352       50.8
5  2009 exp_amt_Year     3635       52.9
6  2009 imp_amt_Year     3230       47.1
7  2010 exp_amt_Year     4663       52.3
8  2010 imp_amt_Year     4252       47.7
9  2011 exp_amt_Year     5552       51.4
10 2011 imp_amt_Year     5244       48.6
11 2012 exp_amt_Year     5478       51.3
12 2012 imp_amt_Year     5195       48.7
13 2013 exp_amt_Year     5596       52.1
14 2013 imp_amt_Year     5155       47.9
15 2014 exp_amt_Year     5726       52.1
16 2014 imp_amt_Year     5255       47.9

 

 

 

 

위의 trade_prop 변수를 활용해서 비율 누적 영역 그래프(Propostion stacked area plot)을 그려보도록 하겠습니다.  값을 기준으로 했을 때와 script는 동일하며, y값 자리에 trade_prop (수출입 무역 비율) 변수로 바꾸어주기만 하면 됩니다.

 

그래프 뒤에 단위 격자가 보이도록 geom_area(alpha=0.5) 로 해서 약간 투명하게 처리했습니다.

 

> # 비율 누적 영역 그래프 그리기
> library(plyr) # desc() 함수 사용 위해 필요
> ggplot(trade_stat_Year_melt_prop, aes(x=Year, y=trade_prop, fill=trade_cd, group=trade_cd, 
+                                  order=desc(trade_cd))) + # 누적 영역 순서 내림차순 정렬
+   geom_area(colour=NA, alpha=0.5) + # alpha 투명도
+   scale_fill_brewer(palette="Blues") +
+   geom_line(position="stack", size=0.3) + 
+   ggtitle("Stacked Area Plot of Trade Proportion (Import, Export) from 2007 to 2014")
ymax not defined: adjusting position using y instead

 

 

 

 

많은 도움이 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 

Posted by R Friend Rfriend

댓글을 달아 주세요

예전 포스팅 중에서 일변량 연속형 변수에 대해 ggplot2로 막대 그래프 그리는 법을 소개했었는데요, 막대 그래프의 훌륭한 대안으로서 점 그래프(Dot Plot)이 있습니다. 

 

Cleveland and McGill (1984) 이  “Graphical Methods for Data Presentation: Full Scale Breaks, Dot Charts, and Multibased Logging.” 이라는 논문에서 막대 그래프 대비 점 그래프가 데이터 해석, 가독성에서 가지는 우수성을 소개하면서 Cleveland Dot Plot 이라고도 많이 불리는 그래프입니다.

 

 

분석에 활용할 데이터는 MASS 패키지 내 Cars93 데이터 프레임에서, 차종(Type), 모델(Model), Max.Price, Min.Price의 4개 변수를 사용하겠으며, 관측치 개수가 많아서 화면 하나에 전부 뿌리기에는 너무 많으므로 차종(Type)의 Level 중에서 "Large", "Midsize", "Small" 만 선별하고 "Compact", "Sproty", "Van"은 제외하도록 하겠습니다.

 

 

> library(MASS)
> str(Cars93)
'data.frame':	93 obs. of  27 variables:
 $ Manufacturer      : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
 $ Model             : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
 $ Type              : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
 $ Min.Price         : num  12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
 $ Price             : num  15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
 $ Max.Price         : num  18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
 $ MPG.city          : int  25 18 20 19 22 22 19 16 19 16 ...
 $ MPG.highway       : int  31 25 26 26 30 31 28 25 27 25 ...
 $ AirBags           : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
 $ DriveTrain        : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
 $ Cylinders         : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
 $ EngineSize        : num  1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
 $ Horsepower        : int  140 200 172 172 208 110 170 180 170 200 ...
 $ RPM               : int  6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
 $ Rev.per.mile      : int  2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
 $ Man.trans.avail   : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
 $ Fuel.tank.capacity: num  13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
 $ Passengers        : int  5 5 5 6 4 6 6 6 5 6 ...
 $ Length            : int  177 195 180 193 186 189 200 216 198 206 ...
 $ Wheelbase         : int  102 115 102 106 109 105 111 116 108 114 ...
 $ Width             : int  68 71 67 70 69 69 74 78 73 73 ...
 $ Turn.circle       : int  37 38 37 37 39 41 42 45 41 43 ...
 $ Rear.seat.room    : num  26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
 $ Luggage.room      : int  11 15 14 17 13 16 17 21 14 18 ...
 $ Weight            : int  2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
 $ Origin            : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
 $ Make              : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...
> table(Cars93$Type)

Compact   Large Midsize   Small  Sporty     Van 
     16      11      22      21      14       9 
> 
> # Model, Type, Max.Price, Min.Price 변수만 선택
> # Type 중에서 Large, Midsize, Small만 선택 (Compact, Sortry, Van은 제외)
> 
> Cars93_P <- subset(Cars93, 
+                      select = c(Model, Type, Min.Price, Max.Price), 
+                      subset = (Type %in% c("Large", "Midsize", "Small")))
> str(Cars93_P)
'data.frame':	54 obs. of  4 variables:
 $ Model    : Factor w/ 93 levels "100","190E","240",..: 49 56 1 6 24 54 74 73 35 79 ...
 $ Type     : Factor w/ 6 levels "Compact","Large",..: 4 3 3 3 3 2 2 3 2 3 ...
 $ Min.Price: num  12.9 29.2 30.8 23.7 14.2 19.9 22.6 26.3 33 37.5 ...
 $ Max.Price: num  18.8 38.7 44.6 36.2 17.3 21.7 24.9 26.3 36.3 42.7 ...
> head(Cars93_P)
    Model    Type Min.Price Max.Price
1 Integra   Small      12.9      18.8
2  Legend Midsize      29.2      38.7
4     100 Midsize      30.8      44.6
5    535i Midsize      23.7      36.2
6 Century Midsize      14.2      17.3
7 LeSabre   Large      19.9      21.7

 

 

 

geom_point() 함수를 사용하여 클리브랜드 점 그래프(Cleveland dot plot)을 그려보겠습니다. 

 

aes(y = reorder(Model, Max.Price)) 를 사용해서 y축에 사용할 Model 을 Max.Price 를 기준으로 정렬을 하였기 때문에 아래처럼 Max.Price가 높은 것부터 낮은 것으로 정렬이 된 채로 점 그래프가 제시되었습니다.

 

aes(shape = Type) 을 적용하여서 Type(Large, Midsize, Small) 별로 모양(shape)을 달리해서 제시하였습니다.

 

> # Cleveland dot plot of Max Price of Models with different shape by Type > library(ggplot2) > > ggplot(Cars93_P, aes(x = Max.Price, y = reorder(Model, Max.Price), shape = Type)) + + geom_point(size = 3, colour = "blue") + + theme_bw() + # background 색 없애기 + theme(panel.grid.major.x = element_blank(), # x축 선 없애기 + panel.grid.minor.x = element_blank(), + panel.grid.major.y = element_line(colour="grey90", linetype="dashed")) + + ggtitle("Cleveland dot plot of Max.Price of Models with different shape by Type")

 

 

 

 

 

 

다음으로, Type(Large, Midsize, Small) 별로 facet_grid(Type ~ ., scales="free_y", space="free_y") 을 적용하여 면을 분할을 한 클리브랜드 점 그래프(Cleveland dot plot)을 그려보겠습니다.

 

면 분할해서 그리려면 위의 예처럼 ggplot2 내 aes(reorder)로는 안되구요, 먼저 Type과 Max.Price 순서대로 데이터셋을 따로 정렬해서 요인(factor)으로 levels 를 지정해서 변환해주어야 합니다.  그래프는 상대적으로 쉬운데, 데이터셋 정렬/요인변환이 어려울 수 있겠습니다.

 

> # Type, Max.Price 순서대로 정렬
> Model_Order <- Cars93_P$Model[order(Cars93_P$Type, # Large, Midsize, Small 순서
+                                     -Cars93_P$Max.Price, # 높은것에서 낮은 순서
+                                     decreasing=TRUE)]
> 
> # Model_Order를 요인(factor)으로 변환
> Cars93_P$Model <- factor(Cars93_P$Model, levels=Model_Order)
> 
> # Type별로 면 분할, Max.Price 순서대로 정렬된 Cleveland dot plot
> ggplot(Cars93_P, aes(x = Max.Price, y = Model)) +
+   geom_point(size = 3, aes(colour = Type)) +
+   theme_bw() +
+   theme(panel.grid.major.y = element_blank(), 
+         panel.grid.minor.y = element_blank()) + 
+   facet_grid(Type ~ ., scales="free_y", space="free_y") +
+   ggtitle("Cleveland dot plot of Max.Price of Models with Facets of Type")

 

 

 

 

 

다음으로, 차종(Type)별로 면 분할은 유지하면서 위의 Max.Price 에 더해서 Min.Price 를 추가하고 모양(shape)을 다르게 제시해보겠습니다.

 

이것도 데이터셋을 따로 미리 손을 봐줘야 합니다.  reshape 패키지의 melt() 함수를 사용해서 Max.Price, Min.Price 두 값을 Price_cd (Max.Price, Min.Price)와 Price (value) 의 두개 변수로 녹여서 데이터 구조를 ggplot2의 geom_point()에 사용할 수 있도록 변경하여야 합니다.  (reshape 패키지의 melt(), cast() 함수는 여기서 자세히 설명하기가 힘든데요, 따로 알아보시면 좋겠습니다)

 

 

> #--------
> # Min.Price 추가 
> # melt
> library(reshape)
> Cars93_P_melt <- melt(Cars93_P, idvars = c("Type", "Model"))
Using Model, Type as id variables
> head(Cars93_P_melt)
    Model    Type  variable value
1 Integra   Small Min.Price  12.9
2  Legend Midsize Min.Price  29.2
3     100 Midsize Min.Price  30.8
4    535i Midsize Min.Price  23.7
5 Century Midsize Min.Price  14.2
6 LeSabre   Large Min.Price  19.9
> 
> # 변수명 변경
> Cars93_P_melt <- rename(Cars93_P_melt, c(variable = "Price_cd", value = "Price"))
> head(Cars93_P_melt)
    Model    Type  Price_cd Price
1 Integra   Small Min.Price  12.9
2  Legend Midsize Min.Price  29.2
3     100 Midsize Min.Price  30.8
4    535i Midsize Min.Price  23.7
5 Century Midsize Min.Price  14.2
6 LeSabre   Large Min.Price  19.9
> 
> # Type별로 면 분할, Max.Price 순서대로 정렬, Min.Price추가된 Cleveland dot plot
> ggplot(Cars93_P_melt, aes(x = Price, y = Model)) + 
+   geom_segment(aes(yend=Model, xend=0)) + # 점까지만 선 그리기
+   geom_point(size=3, aes(shape = Price_cd)) + # Price_cd로 모양 구분
+   theme_bw() + # backgroud 색 없애기
+   theme(panel.grid.major.y = element_blank(), # y축 없애기
+         panel.grid.minor.y = element_blank()) + # y축 없애기
+   facet_grid(Type ~ ., scales="free_y", space="free_y") + # Type별로 면 분할
+   ggtitle("Cleveland dot plot of Max, Min Price of Models with Facets of Type")
 

 

 

 

위의 세번째 그래프처럼 Max.Price와 Min.Price를 같은 그래프에 그리는데, 만약 이것을 막대 그래프로 그린다고 상상해 보세요.  막대그래프로 그린다면 지저분하고 해석, 가독성이 클리브랜드 점 그래프 대비 떨어질겁니다. 

 


 

[Reference]

 

Cleveland, William S. 1984. “Graphical Methods for Data Presentation: Full Scale
Breaks, Dot Charts, and Multibased Logging.” The American Statistician, 38:270-280.

 

Dot Plots: A Useful Alternative to Bar Charts, Naomi B. Robbins, Ph.D. March 7, 2006

 

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

Posted by R Friend Rfriend

댓글을 달아 주세요

  1. 김경찬 2017.02.04 17:02  댓글주소  수정/삭제  댓글쓰기

    와~ 점그래프 그리는건 정말 어렵네요. segment함수의 매개변수 왜 저렇게 두는지도 모르겠고 ㅎㅎ 외우기는 정말 어려운듯하네요ㅠㅠ

    • R Friend Rfriend 2017.02.04 17:23 신고  댓글주소  수정/삭제

      젇 함수를 전부 다 외우지는 못합니다. 필요할 때 블로그 보면서 만들고 싶은 형태 골라서 R scriot 짜요.

      클리브랜드 점 그래프 유용해서 종종 쓰은 편이예요.

      엑셀 그래프 대비 R이 좋은 점이 바로 이런거죠. 데이터 전처리 하고 세부 옵션으로 조정해 가면서 바로 인쇄에 써먹어도 될 정도의 고품질 그래프를 스릴 수 있다는거요.

    • HG 2020.12.27 18:26  댓글주소  수정/삭제

      검색해보니까 geom_segment function은 점을 기준으로 그리기 때문에 그렇네요

    • R Friend Rfriend 2020.12.27 23:50 신고  댓글주소  수정/삭제

      HG님, 댓글 감사합니다! :-)

 

2개의 연속형 변수를 가지고 그릴 수 있는 그래프 중에 이차원 밀도 그래프 (2D Density Plot) 을 ggplot2의 stat_density2d() 함수를 이용하여 그려보겠습니다. 

 

 

이차원 밀도 그래프 (2D Density Plot)은 2D 커널 밀도 추정치를 구해서 같은 추정치를 선으로 연결한 그래프 입니다. 

 

 

우리가 일상 생활 중에 자주 쉽게 접하는 이차원 밀도 그래프의 예로는 지도의 등고선이나 일기예보할 때 쓰는 등압선이 있습니다.

 

 

[ 기상청 등압선 얘시 ]

 

 

* 출처: 기상청, http://www.kma.go.kr/weather/images/analysischart.jsp

 

 

이번에 R로 예를 들  데이터는 뉴욕의 기상을 19 73년 5월부터 9월까지 매일 측정한 airquality 데이터 프레임에서 5월달과 7월달 두 달의 Wind, Temp를 사용하겠습니다.

 

(airquality는 시계열 데이터이고, 이전 포스팅에서 airquality 데이터 프레임을 사용하여 ggplot2로 시계열 그래프 그래프 그리는 법 설명하였으니 참고하시기 바랍니다)

 

먼저, airquality의 데이터 구조를 살펴보고, 5월달과 7월달 데이터만 선별해서 새로운 데이터 프레임을 만들어보겠습니다.

 

 

> # airquality 구조
> str(airquality)
'data.frame':	153 obs. of  6 variables:
 $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
 $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
 $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
 $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...
 $ Month  : int  5 5 5 5 5 5 5 5 5 5 ...
 $ Day    : int  1 2 3 4 5 6 7 8 9 10 ...
> 
> # 5월과 7월만 선택
> airquality_May_July <- subset(airquality, 
+                               select = c(Month, Day, Wind, Temp), 
+                               subset = (Month %in% c(5, 7)))
 
>
>
> head(airquality_May_July)
  Month Day Wind Temp
1     5   1  7.4   67
2     5   2  8.0   72
3     5   3 12.6   74
4     5   4 11.5   62
5     5   5 14.3   56
6     5   6 14.9   66
>
>
> tail(airquality_May_July)
   Month Day Wind Temp
87     7  26  8.6   82
88     7  27 12.0   86
89     7  28  7.4   88
90     7  29  7.4   86
91     7  30  7.4   83
92     7  31  9.2   81

 

 

 

이제 이차원 밀도 그래프 (2D Density Plot)을 그려보겠습니다.  그리고 5월과 7월달의 Month를 색깔로 구분하여 보겠습니다.

 

이때 조심해야 할 것이 있습니다.  aes() 에 shape이나 colour 에는 범주형변수(factor)가 들어가야 합니다.  만약 연속형 변수가 들어가면  "Error: A continuous variable can not be mapped to shape" 라는 에러 메시지가 뜹니다.

 

> # 2차원 밀도 그래프 : 모양과 색깔로 구분
> # 연속형 변수라서 error
> ggplot(data=airquality_May_July, aes(x=Wind, y=Temp, shape=Month)) +
+   geom_point() + 
+   stat_density2d() +
+   ggtitle("2D desity plot of Wind and Tmep, at May and July")
Error: A continuous variable can not be mapped to shape

 

 

 

Month를 Month.ch라는 새로운 문자형 변수로 변환해, 이를 사용해서 이차원 밀도 그래프를 Month별로 모양과 색깔을 구분해서 그려보겠습니다.  

 

> # Month를 문자형 변수로 변환
> airquality_May_July <- transform(airquality_May_July, Month.ch = as.character(Month))
> 
> sapply(airquality_May_July, class)
      Month         Day        Wind        Temp    Month.ch 
  "integer"   "integer"   "numeric"   "integer" "character"
> 
> head(airquality_May_July)
  Month Day Wind Temp Month.ch
1     5   1  7.4   67        5
2     5   2  8.0   72        5
3     5   3 12.6   74        5
4     5   4 11.5   62        5
5     5   5 14.3   56        5
6     5   6 14.9   66        5

 

 

 

stat_density2d() 함수로 커널 밀도 추정치를 계산해서 2차원 밀도 그래프를 그리면,

 

> # 2차원 밀도 그래프 : Month를 모양으로 구분
> ggplot(data=airquality_May_July, aes(x=Wind, y=Temp, shape=Month.ch)) +
+   geom_point(size=4) + 
+   stat_density2d() +
+   ggtitle("2D desity plot of Wind and Tmep, May/July by Shape")
> 

 

 


 

> # 2차원 밀도 그래프 : Month를 색깔로 구분 > ggplot(data=airquality_May_July, aes(x=Wind, y=Temp, colour=Month.ch)) + + geom_point(size=4) + + stat_density2d() + + ggtitle("2D desity plot of Wind and Tmep, at May/July by Colour")

 

 

 

 

 

 

 

 

이번에는 (범례가 있기는 합니다만) 사용자의 가독성을 조금 더 높여주기 위해 2차원 밀도 그래프의 5월, 7월 두 집단의 중앙 부위에 년/월을 annotate()의 "text"로 라벨을 추가해 보겠습니다.

 

> # 2차원 밀도 그래프 : Month를 색깔로 구분, 년/월 라벨 추가
> ggplot(data=airquality_May_July, aes(x=Wind, y=Temp, colour=Month.ch)) +
+   geom_point(size=4) + 
+   stat_density2d() +
+   ggtitle("2D desity plot of Wind and Tmep, at1973. May/July by Colour") +
+   annotate("text", x=11, y=65, label="1973.May", alpha=0.5) + 
+   annotate("text", x=9, y= 83, label="1973.July", alpha=0.5)

 

 

 

 

 

많은 도움 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

Posted by R Friend Rfriend

댓글을 달아 주세요

변수의 개수 및 데이터의 형태에 따라서 그래프, 시각화 방법이 달라지는데요,

 

지난번 포스팅에서는 한 변수의 연속형 데이터의 시각화 방법으로

 

 - 히스토그램(Histogram)
    : geom_histogram()

- 커널 밀도 곡선(Kernel Density Curve)
    : geom_density()

 - 박스 그래프(Box Plot)
    : geom_boxplot()

 - 바이올린 그래프(Violin Plot)
    : geom_violin()

 

범주형 데이터에 대한 시각화 방법으로

 

 - 막대그림(Bar Chart): geom_bar()

 - 원그림(Pie Chart): geom_bar() + coord_polar()

 - 모자이크 그림(Mosaic Chart): vcd 패키지 mosaic()

 

알아보았습니다.

 

 

이번에는 두 개 이상의 연속형 변수를 시각화하는 방법으로

 

 - 산점도 (Scatter Plot): geom_point()

 - 선 그래프(Line Plot): geom_line()

 - 시계열 그래프(Time Series Plot): geom_line()  

 

에 대해서 알아보겠습니다.

 

 

[ 변수 개수 및 형태에 따른 그래프 종류 ]

 

 

 

 

 

선 그래프(Line Graph)는 x축의 연속형 변수(또는 (순서나 크기가 있는 이산형 변수, ordered factor)의 변화에 따른 y축의 변화를 선으로 이어서 보여주는 그래프입니다.  x축이 시간의 순서이면 시계열 그래프(Time Series Graph) 이구요.

 

 

Base 패키지에 들어있는 airquality 데이터 프레임을 가지고 시계열 그래프를 그려보도록 하겠습니다.

airquality 데이터 프레임은 1973년 5월부터 9월달까지 뉴욕의 공기의 Ozone, Solar, Wind, Temp를 Daily 로 측정한 데이터 셋입니다.  결측값이 없는 Wind와 Temp를 가지고 시계열 그래프를 그려보겠습니다.

 

> str(airquality)
'data.frame':	153 obs. of  6 variables:
 $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
 $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
 $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
 $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...
 $ Month  : int  5 5 5 5 5 5 5 5 5 5 ...
 $ Day    : int  1 2 3 4 5 6 7 8 9 10 ...
 
> head(airquality)
  Ozone Solar.R Wind Temp Month Day
1    41     190  7.4   67     5   1
2    36     118  8.0   72     5   2
3    12     149 12.6   74     5   3
4    18     313 11.5   62     5   4
5    NA      NA 14.3   56     5   5
6    28      NA 14.9   66     5   6

 

> # 결측값 여부 확인
> sum(is.na(airquality$Ozone))
[1] 37
> sum(is.na(airquality$Solar.R))
[1] 7
> sum(is.na(airquality$Wind))
[1] 0
> sum(is.na(airquality$Temp))
[1] 0

 

 

보기에 좋도록 Month 변수와 Day 변수를 합쳐서 Time이라는 새로운 변수를 만들어보겠습니다.

 

> # Time 변수 생성
> airquality <- transform(airquality, 
+                         Month.ch_temp = as.character(Month), 
+                         Day.ch_temp = as.character(Day))
> 
> airquality <- transform(airquality, 
+                         Month.ch = paste("0", Month.ch_temp, sep=""), 
+                         Day.ch = ifelse(Day.ch_temp == "1", "01", 
+                                  ifelse(Day.ch_temp == "2", "02", 
+                                  ifelse(Day.ch_temp == "3", "03", 
+                                  ifelse(Day.ch_temp == "4", "04", 
+                                  ifelse(Day.ch_temp == "5", "05", 
+                                  ifelse(Day.ch_temp == "6", "06", 
+                                  ifelse(Day.ch_temp == "7", "07", 
+                                  ifelse(Day.ch_temp == "8", "08", 
+                                  ifelse(Day.ch_temp == "9", "09", 
+                                                  Day))))))))))
> 
> airquality <- transform(airquality, 
+                         Time = paste(Month.ch, Day.ch, sep=""))
> 
> airquality_May <- airquality[c(1:31), c(1:6, 11)]
> 
> head(airquality_May)
  Ozone Solar.R Wind Temp Month Day Time
1    41     190  7.4   67     5   1 0501
2    36     118  8.0   72     5   2 0502
3    12     149 12.6   74     5   3 0503
4    18     313 11.5   62     5   4 0504
5    NA      NA 14.3   56     5   5 0505
6    28      NA 14.9   66     5   6 0506

 

 

 

선그래프를 그릴 때 group 지정을 안해주면 아래처럼 경고메시지가 뜨고, 선그래프가 텅 비게 나옵니다.

 

* 경고메시지: geom_path: Each group consist of only one observation. Do you need to adjust the group aesthetic?

 

> # 선그래프
> library(ggplot2)
> 
> # warning: geom_path: Each group consist of only one observation. Do you need to adjust the group aesthetic?
>
> ggplot(airquality_May, aes(x=Time, y=Wind)) + 
+   geom_line() + 
+   ggtitle("Line Graph, Wind from May.01 to May.31")
geom_path: Each group consist of only one observation. Do you need to adjust the group aesthetic?

 

 

 

 

 

이 문제를 해결하기 위해 gruop=1 을 지정해줍니다.

 

> # group=1
> ggplot(airquality_May, aes(x=Time, y=Wind, group=1)) + 
+   geom_line() + 
+   ggtitle("Time Series Graph, Wind from May.01 to May.31")

 

 

 

 

 


> ggplot(airquality_May, aes(x=Time, y=Temp, group=1)) + 
+   geom_line() + 
+   ggtitle("Time Series Graph, Temp from May.01 to May.31")

 

 

 

 

 

 

위의 두 개의 시계열 그래프를 보면 y축을 ggplot2 가 자동으로 계산해서 적정 범위를 설정해주었습니다만, 경우에 따라서는 사용자가 강제로 특정 범위를 설정해주고 싶을 때가 있습니다.  아래는 ylim()을 이용해서 0~max(y) 값 까지 y축 범위를 설정한 예시입니다.

 

> # y축 범위 설정 0~max
> ggplot(airquality_May, aes(x=Time, y=Temp, group=1)) + 
+   geom_line() + 
+   ylim(0, max(airquality_May$Temp)) +
+   ggtitle("Time Series Graph, Temp from May.01 to May.31, y axis from 0 to max")

 

 

 

 

 

 

 

선그래프의 선 모양도 바꿔보고 싶고, 점도 덮입혀 보고 싶다면 아래 예제를 참고하세요.

 

> # 점선으로 변경, 점 추가
> ggplot(airquality_May, aes(x=Time, y=Temp, group=1)) + 
+   geom_line(linetype="dotted", size=1, colour="blue") + 
+   geom_point(size=3, shape=19, colour="blue") +
+   ggtitle("Time Series Graph, Temp from May.01 to May.31, with dotted line, dot")

 

 

 

 

 

참고로 R Line Type과 Shape 은 아래와 같습니다.

 

[ R Line Type ]

 

 

 

[ R Symbol Shape ]

 

 

 

다음은 aes() 내 group과 colour 를 사용하여 그룹별로 선그래프, 시계열그래프 그리는 방법입니다. 

 

> # 집단별 시계열 그래프 : 월별(5월~9월) Temp 시계열 그래프
> ggplot(data=airquality, aes(x=Day, y=Temp, colour=Month, group=Month)) + 
+   geom_line() + 
+   geom_point(size=3) +
+   ggtitle("Time Seires Graph, Temp from May to Sep.")

 

 

 

 

많은 도움 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

Posted by R Friend Rfriend

댓글을 달아 주세요

  1. ggplot 2018.01.28 23:27  댓글주소  수정/삭제  댓글쓰기

    group = 1 이 이해가 잘 되지 않습니다. group = 2나 0이나 결과는 같네요..??

변수의 개수 및 데이터의 형태에 따라서 그래프, 시각화 방법이 달라지는데요,

 

지난번 포스팅에서는 한 변수의 연속형 데이터의 시각화 방법으로

 

 - 히스토그램(Histogram)
    : geom_histogram()

- 커널 밀도 곡선(Kernel Density Curve)
    : geom_density()

 - 박스 그래프(Box Plot)
    : geom_boxplot()

 - 바이올린 그래프(Violin Plot)
    : geom_violin()

 

범주형 데이터에 대한 시각화 방법으로

 

 - 막대그림(Bar Chart): geom_bar()

 - 원그림(Pie Chart): geom_bar() + coord_polar()

 - 모자이크 그림(Mosaic Chart): vcd 패키지 mosaic()

 

알아보았습니다.

 

 

이번에는 두 개 이상의 연속형 변수를 시각화하는 방법으로

 

 - 산점도 (Scatter Plot): geom_point()

 - 선 그래프(Line Plot): geom_line()

 - 시계열 그래프(Time Series Plot): geom_line()  

 

에 대해서 알아보겠습니다.

 

참고로 ☞ ggplot2의 geom_point() 산점도 그리기 

          ☞ Base Graphics 패키지의 pairs() 함수를 사용한 산점도 행렬 그리기

 

 

[ 변수 개수 및 형태에 따른 그래프 종류 ]

 

 

 

 

산점도(Scatter Plot)는 x축과 y축에 연속형인 두 변수의 값을 점으로 뿌려준 그래프로서, 연속형인 두 변수 간의 관계를 파악하는데 유용합니다.  다중회귀분석을 할 때 제일 처음 하는 일이 바로 산점도 (행렬)을 그려보고 두 변수간의 선형성 여부를 탐색해보는 일입니다.

 

MASS패키지 내 Cars93 데이터 프레임의 고속도로연비(MPG.highway)와 엔진크기(EngineSize), 무게(Weight), 길이(Length) 와의 관계를 ggplot2 패키지의 geom_point() 함수를 가지고 산포도를 그려서 알아보도록 하겠습니다.   그리고 차종(Type)별로 고속도로연비(MPG.highway) 는 어떻게 되는지도 산포도를 가지고 점의 색깔과 모양을 달리해서 보는 방법을 알아보겠습니다.

 

 

> library(MASS)
> str(Cars93)
'data.frame':	93 obs. of  27 variables:
 $ Manufacturer      : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
 $ Model             : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
 $ Type              : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
 $ Min.Price         : num  12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
 $ Price             : num  15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
 $ Max.Price         : num  18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
 $ MPG.city          : int  25 18 20 19 22 22 19 16 19 16 ...
 $ MPG.highway       : int  31 25 26 26 30 31 28 25 27 25 ...
 $ AirBags           : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
 $ DriveTrain        : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
 $ Cylinders         : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
 $ EngineSize        : num  1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
 $ Horsepower        : int  140 200 172 172 208 110 170 180 170 200 ...
 $ RPM               : int  6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
 $ Rev.per.mile      : int  2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
 $ Man.trans.avail   : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
 $ Fuel.tank.capacity: num  13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
 $ Passengers        : int  5 5 5 6 4 6 6 6 5 6 ...
 $ Length            : int  177 195 180 193 186 189 200 216 198 206 ...
 $ Wheelbase         : int  102 115 102 106 109 105 111 116 108 114 ...
 $ Width             : int  68 71 67 70 69 69 74 78 73 73 ...
 $ Turn.circle       : int  37 38 37 37 39 41 42 45 41 43 ...
 $ Rear.seat.room    : num  26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
 $ Luggage.room      : int  11 15 14 17 13 16 17 21 14 18 ...
 $ Weight            : int  2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
 $ Origin            : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
 $ Make              : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...

 

 

상관계수를 가지고 고속도로연비(MPG.highway)와 엔진크기(EngineSize), 무게(Weight), 길이(Length) 와의 상관도를 먼저 살펴보면, 고속도로연비와 엔진크기, 무게, 길이가 모두 역의 상관관계가 있는 걸로 나왔고, 특히 무게가 역의 상관관계가 크게 나왔습니다.  

 

> Cars93_MPG <- Cars93[,c("MPG.highway", "EngineSize", "Weight", "Length")]
> cor(Cars93_MPG)
            MPG.highway EngineSize     Weight     Length
MPG.highway   1.0000000 -0.6267946 -0.8106581 -0.5428974
EngineSize   -0.6267946  1.0000000  0.8450753  0.7802831
Weight       -0.8106581  0.8450753  1.0000000  0.8062743
Length       -0.5428974  0.7802831  0.8062743  1.0000000 

 

 

이제 산점도를 그려서 고속도로연비(MPG.highway)와 엔진크기(EngineSize), 무게(Weight), 길이(Length) 관계를 살펴보겠습니다.

 

제일 쉬운 방법은 Base graphics 패키지에 있는 plot()함수를 사용하는 방법입니다.  위에서 분석하려는 변수만 따로 선별해놓은 Cars93_MPG 데이터 프레임을 가지고 한번 산점도 행렬을 그려보겠습니다.

 

> plot(Cars93_MPG, 
+      main="Scatter Plot Matrix")

 

 

 

 

 

 

ggplot2로는 산점도 행렬(Scatter Plot matrix)를 그리는 것이 힘듭니다.  대신 여러 조건을 주어서 두 변수 간 산점도 행렬을 다양하게 그려보는데는 아주 강력합니다   우선 ggplot2의 geom_point()함수를 가지고 색깔(colour)과 모양(shape)을 달리하면서 산점도를 그려보겠습니다.

 

> # Scatter Plot: MPG.highway vs. EngineSize, Weight, Length
> library(ggplot2)
> 
> ggplot(data=Cars93, aes(x=EngineSize, y=MPG.highway)) + 
+   geom_point(shape=15, size=3, colour="blue") + # shape 15: solid square
+   ggtitle("Scatter Plot: MPG.highway vs. EngineSize")

 

 

 

 
 

 

> ggplot(data=Cars93, aes(x=Weight, y=MPG.highway)) + 
+   geom_point(shape=19, size=3, colour="red") + # shape 19: solid circle
+   ggtitle("Scatter Plot: MPG.highway vs. Weight")

 

 

 

 

 

 

> ggplot(data=Cars93, aes(x=Length, y=MPG.highway)) + 
+   geom_point(shape=24, size=3, colour="black") + # shape 24: filled triangle point-up
+   ggtitle("Scatter Plot: MPG.highway vs. Length")

 

 

 

 

 

 

참고로, R plot의 숫자별 모양은 다음과 같습니다.

 

> # R plot symbols: points
> help(pch)

 

 

 

 

 

 

이번에는 두 변수의 산포도에 모델명 라벨을 geom_text(label=) 함수를 이용하여 입혀보겠습니다.  

 

> # Scatter Plot with Label (Model Name) > ggplot(data=Cars93, aes(x=Weight, y=MPG.highway)) + + geom_point(shape=19, size=3, colour="red") + # shape 19: solid circle + ggtitle("Scatter Plot: MPG.highway vs. Weight with Model Label") + + geom_text(aes(label=Model, size=2, vjust=-1, hjust=0)) # vjust=-1 위로, hjust=1 오른쪽

 

 

 

 

 

 

 

다음으로 차종(Type)별로 구분하여서 무게(Weight)와 고속도로연비(MPG.highway) 간의 관계를 3가지 방법을 사용하여 산포도로 그려보도록 하겠습니다.

 

     (1) 차종(Type)별로 색깔(colour)을 달리해서

     (2) 차종(Type)별로 모양(shape)을 달리해서

     (3) 차종(Type)별로 층(facet_grid)을 나누어서

산포도를 그려보겠습니다.

 

 

(1) 차종(Type)별로 색깔(colour)을 달리했을 때 

 

> # Scatter Plot by Type, using different Colours
> ggplot(data=Cars93, aes(x=Weight, y=MPG.highway, colour=Type)) + 
+   geom_point(shape=19, size=3) + 
+   ggtitle("Scatter Plot by Type, using different Colours")

 

 

 

 

 

 

 (2) 차종(Type)별로 모양(shape)을 달리했을 때

 

> # Scatter Plot by Type, differenct Shapes
> ggplot(data=Cars93, aes(x=Weight, y=MPG.highway, shape=Type)) + 
+   geom_point(size=3) + 
+   ggtitle("Scatter Plot by Type, differenct Shapes")

 

 

 

 

 

(3) 차종(Type)별로 층(facet_grid)을 나누어서 산포도를 그릴 때 

 

> # Scatter Plot by Type, using facet_grid
> ggplot(data=Cars93, aes(x=Weight, y=MPG.highway)) + 
+   geom_point(size=3, shape=19) + 
+   facet_grid(Type ~.) +
+   ggtitle("Scatter Plot by Type, using facet_grid")

 

 

 

 

 

 

다음으로, 산포도에 선형 회귀선 (신뢰구간 95%)을 적합시켜보겠습니다.

 

> # Scatter Plot with linear regression line
> ggplot(data=Cars93, aes(x=Weight, y=MPG.highway)) + 
+   geom_point(shape=19, size=3, colour="red") + # shape 19: solid circle 
+   stat_smooth(method=lm, level=0.95) + 
+   ggtitle("Scatter Plot: Linear Regression Line with Confidence Level 95%")
 

 

 

 

아래는 산포도에 회귀선을 적합시켰는데, 위와는 다르게 신뢰구간은 뺀 경우입니다.

 

> ggplot(data=Cars93, aes(x=Weight, y=MPG.highway)) + 
+   geom_point(shape=19, size=3, colour="red") + # shape 19: solid circle 
+   stat_smooth(method=lm, se=FALSE) + 
+   ggtitle("Scatter Plot: Linear Regression Line without Confidence Level")

 

 

 

 

 

 

산포도에 선을 적합시킬 때 선형회귀선말고도 loess(locally weighted polynomial) 를 써서 비모수 회귀(Nonparametric regression) 선을 적합시킬 수도 있습니다.

 

> # Scatter Plot with loess(locally weighted polynomial)
> ggplot(data=Cars93, aes(x=Weight, y=MPG.highway)) + 
+   geom_point(shape=19, size=3, colour="red") + # shape 19: solid circle 
+   stat_smooth(method=loess, level=0.95) + 
+   ggtitle("Scatter Plot: loess Line with Confidence Level 95%")

 

 

 

 

많은 도움 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 

Posted by R Friend Rfriend

댓글을 달아 주세요

  1. Taecong 2019.11.08 22:36  댓글주소  수정/삭제  댓글쓰기

    선생님 명령어 그대로 복사 붙여넣기 하면 그림이 안나와요..

    Error: Cannot use `+.gg()` with a single argument. Did you accidentally put + on a new line?

    이런 에러만 자꾸 뜨네요 ㅜ

    • R Friend Rfriend 2019.11.08 22:41 신고  댓글주소  수정/삭제

      아, 그것은 앞에 '+' 부호를 빼고 돌리면 됩니다.

      RStudio에서 실행하면 console 창에는 원래 코드에 줄바꿈될 때 '+' 부호가 따라서 붙는데요, 제가 편집창이 아닌 console창의 실행 후 코드('+' 가 붙은) 를 복사해서 붙여서 그런 것입니다.

      포스팅 본문에서 줄바꿈 될 때마다 앞에 붙어있는 '+' 부호를 떼어내고서 다시 돌려보시기 바랍니다.

      단 코드 줄마다 제일 뒤의 '+' 부호는 그대로 두시구요.