이번 포스팅에서는 방향을 가진 네트워크 상에서 유량, 에너지, 물자 등의 '네트워크(network) 기반'의 흐름(flow)을 시각화하는데 유용한 Sankey Diagram 에 대해서 알아보겠습니다. 

 

Sankey Diagram 은 얼핏보면 평행좌표그림(parallel coordinate plot)과 비슷한 면이 있습니다.  하지만 Sankey Diagram은 Node -> Edge 로의 관계, 경로가 있다는 점, 경로의 두께를 weight 에 따라서 다르게 한다든지, 색깔을 부여할 때도 반투명(alpha)하게 한다든지 해서 평행좌표그림 대비 다릅니다. (평행좌표그림은 그냥 다변량 변수들을 y축 높이를 같게 해서 옆으로 x를 죽 늘어놓은 형태. 경로 개념 없음. 선 두께 동일) 

 

아래 이미지는 구글에서 Sankey Diagram 이라고 키워드 검색했을 때 나오는 이미지들을 화면캡쳐한 것입니다.  아래 이미지를 보면 '아, 이거~ ' 싶으시죠?

 

 

* 이미지 출처 : Google 검색

 

 

자, 그럼

 

 - R의 riverplot package(* author : January Weiner) 를 가지고

 - minard 데이터셋(* source : Charles Joseph Minard)을 사용해서
   (R riverplot package에 내장되어 있음)

 

나폴레옹 군대가 러시아로 진군했다가 퇴각했던 경로를 시각화해보겠습니다.

 

 

분석의 재미를 더하기 위해 "minard" 데이터셋에 대한 역사적인 배경을 간략히 알아보고 넘어가겠습니다.

 

때는 바야흐로 1812년, 유럽이 되겠습니다.  프랑스의 나폴레옹은 유럽의 상당 국가를 점령했으며, 영국을 침탈하기 위해 백방으로 쌈질을 걸었지만 번번히 실패하고 있던 상황이었습니다.  이에 화가 난 나폴레옹은 '바다를 건너가서 싸우는 게 승산이 낮으니 차라리 유럽 본토와 영국과의 무역을 봉쇄해서 영국의 피를 말리자. 그래, 바로 이거야. 내 사전에 불가능은 없어!' 라는 계획을 세우게 됩니다.

 

그러나 but,

 

나폴레옹의 프랑스가 이미 과도하게 힘이 세졌기 때문에 견제가 필요하다가 생각한 러시아의 알렉산더 짜르는 '나폴레옹, 내가 니 봉이냐?  영국 다음에 러시아 공격할거지?  내가 누구 좋으라고 니 말을 따라?' 라면서 나폴레옹의 영국과의 무역 폐쇄령을 쌩까고 게기게 됩니다. 

 

이에 발끈한 나폴레옹은 프랑스 대군을 모집해서 러시아 짜르의 못되고 괴씸한(?) 버릇을 고쳐주고자, 본떼를 보여주고자 1812년 10월, 겨울이 코앞인 시점에 러시아 모스코바로 진격을 하게 됩니다. 이때만 해도 정말 나폴레옹 군대는 "진격의 거인" 이었습니다.

 

그러나 but,

 

러시아 알렉산더 짜르가 대책없이 나폴레옹에게 대든게 아니었습니다.  러시아 짜르는 러시아 군과 국민에게 아주 간단한(?) 전략의 명령을 내립니다.  "프랑스군이 진격하는 곳의 모든 것을 태워서 프랑스군이 아무것도 탈취하지 못하도록 하고, 싸우지는 말고 퇴각하라.  전투 전략 끝!"

 

아마, 나폴레옹의 프랑스군은 처음 며칠은 러시아로 무혈입성하는 것에 신이 났을지도 모릅니다.

 

그러나 but,

 

10월이 11월이 되고, 그 담에 12월이 되면서 추위와 배고픔과 질병에 프랑스 군인들의 대부분이 죽어나갔습니다.  진격의 거인 나폴레옹은 러시아 짜르의 "불태우고 후퇴" 전략에 속수무책으로 당하면서 거의 전멸을 당하게 됩니다.

 

 

 

아래의 그림이 나폴레옹 프랑스 군대가 러시아로 진격했다가 퇴각한 진로를 지도 상에 표기한 것입니다.

 

* 출처 : https://robots.thoughtbot.com/analyzing-minards-visualization-of-napoleons-1812-march

 

 

 

여기까지의 이야기를 토대로 숫자와 그래프를 가지고 한눈에 실감할 수 있는 시각화를 Charles Joseph Minard 이라는 분이 아래와 같이 했습니다.

 

[ Napoleon army march : minard ]

* 출처 : https://robots.thoughtbot.com/analyzing-minards-visualization-of-napoleons-1812-march

 

 

 

위의 2차원의 minard visualization에는 다양한 차원의 정보가 알차게(!) 들어있는데요,


 - (1) 프랑스군의 진격과 퇴각 경로 (advance and retreat path and direction)

        : 연한 색깔이 진격(advance), 검정 색깔이 퇴각(retreat)

        : 도시 이름은 좌표에 따라 text로 표기

        : 프랑스 군이 세갈래(하나의 큰 줄기, 두 개의 얇은 줄기)로 나누어 진격한 것도 선이 갈라지게 표현

 - (2) 프랑스군의 인명 손실 규모 (loss of life at a time and location)
        : 선의 두께(line width), 처음에는 몽둥이처럼 두꺼웠던 선이 퇴각 마무리 시점에는 실처럼 가늘게 됨

 

 - (3) 온도 (temperature)

        : 그림 하단에 퇴각 시점의 온도를 그리 넣음.  최저 -30도씨까지 떨어졌음.  얼어죽기 딱 좋은 날씨. -_-;

 

 - (4) 강 (river)

        : 하단에 얇은 수직 선으로 나폴레옹군이 퇴각 시점에 맞닥트려 시련을 더해 준 강(river)을 그려 넣음. 

 

 

이렇게 많은 알찬 정보를 저 위의 시각화 하나에 오롯히 담아 냈습니다!!!  이해하기 쉽죠?!

 

 

이걸 데이터로 나타내 보면 아래와 같습니다.  @@;  

이게 무슨 소린가, 데이터가 뭘 말해주려고 하나.... 눈 돌아가지요? 

위의 그래프로 보면 단박에 이해되는 것을 아래의 숫자로 보면 한숨 나오고 갑갑하지요? ㅋㅋ

 

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

 

> data( minard )
> minard
$edges
    ID1  ID2  Value direction
1    A1   A2 422000         A
2    A2   A3 400000         A
3    A3   A4 320000         A
4    A4   A5 320000         A
5    A5   A6 300000         A
6    A6   A7 280000         A
7    A7   A8 240000         A
8    A8   A9 210000         A
9    A9  A10 180000         A
10  A10  A11 175000         A
11  A11  A12 145000         A
12  A12  A13 140000         A
13  A13  A14 127100         A
14  A14  A15 100000         A
15  A15  A16 100000         A
16  A16   R1 100000         A
17   R1   R2 100000         R
18   R2   R3  98000         R
19   R3   R4  97000         R
20   R4   R5  96000         R
21   R5   R6  87000         R
22   R6   R7  55000         R
23   R7   R8  37000         R
24   R8   R9  24000         R
25   R9  R10  20000         R
26  R10  R11  50000         R
27  R11  R12  50000         R
28  R12  R13  48000         R
29  R13  R14  20000         R
30  R14  R15  12000         R
31  R15  R16  14000         R
32  R16  R17   8000         R
33  R17  R18   4000         R
34  R18  R19  10000         R
35  R19  R19  10000         R
36   A3 A4.2  60000         A
37 A4.2 A5.2  40000         A
38 A5.2 A6.2  33000         A
39 A6.2  R10  30000         R
40   A2 A3.1  22000         A
41 A3.1  R18   6000         A

$nodes
       ID Longitude Latitude
A1     A1      24.0     54.9
A2     A2      24.5     55.0
A3     A3      25.5     54.5
A4     A4      26.0     54.7
A5     A5      27.0     54.8
A6     A6      28.0     54.9
A7     A7      28.5     55.0
A8     A8      29.0     55.1
A9     A9      30.0     55.2
A10   A10      30.3     55.3
A11   A11      32.0     54.8
A12   A12      33.2     54.9
A13   A13      34.4     55.5
A14   A14      35.5     55.4
A15   A15      36.0     55.5
A16   A16      37.6     55.8
R1     R1      37.7     55.7
R2     R2      37.5     55.7
R3     R3      37.0     55.0
R4     R4      36.8     55.0
R5     R5      35.4     55.3
R6     R6      34.3     55.2
R7     R7      33.3     54.8
R8     R8      32.0     54.6
R9     R9      30.4     54.4
R10   R10      29.2     54.3
R11   R11      28.5     54.2
R12   R12      28.3     54.3
R13   R13      27.5     54.5
R14   R14      26.8     54.3
R15   R15      26.4     54.4
R16   R16      25.0     54.4
R17   R17      24.4     54.4
R18   R18      24.2     54.4
R19   R19      24.1     54.4
A4.2 A4.2      26.6     55.7
A5.2 A5.2      27.4     55.6
A6.2 A6.2      28.7     55.5
A3.1 A3.1      24.6     55.8

$cities
   Longitude Latitude           Name
1       24.0     55.0          Kowno
2       25.3     54.7          Wilna
3       26.4     54.4       Smorgoni
4       26.8     54.3      Moiodexno
5       27.7     55.2      Gloubokoe
7       28.5     54.3     Studienska
8       28.7     55.5        Polotzk
9       29.2     54.4           Bobr
10      30.2     55.3        Witebsk
11      30.4     54.5         Orscha
13      32.0     54.8       Smolensk
14      33.2     54.9    Dorogobouge
15      34.3     55.2          Wixma
16      34.4     55.5          Chjat
17      36.0     55.5        Mojaisk
18      37.6     55.8         Moscou
19      36.6     55.3      Tarantino
20      36.5     55.0 Malo-Jarosewii

 

 

* source : R riverplot package, author : January Weiner, data source : Charles Joseph Minard

 

 

 Sankey Diagram에서 사용하는 node, edge라는 용어를 이해하기 위해서, 두 개체 간 쌍을 이룬 관계 (mathematical structures used to model pairwise relations between objects)를 다루는 Graph Theory에 대해서 간략히 짚고 넘어가겠습니다.

 

아래 그래프처럼 점(Node or Point or Vertice)과 선(Edge or Link or Line or Arc)으로 개체 간의 관계를 나타내는 그래프로 나타내어 연구하는 수학, 컴퓨터 과학 분야가 Graph theory입니다.  최적화(optimization) 할 때도 네트워크 그래프 많이 쓰곤 합니다.

 

 

 

 

Sankey Diagram 그리려면

 

  - Nodes : 개체들의 ID, Longitude, Latitude, Labels

  - Edges : 개체 간 관계를 나타내는 ID 1, ID 2, Value (or weight) 

 

정보가 필요합니다.

 

> str(minard)
List of 3
 $ edges :'data.frame':	41 obs. of  4 variables:
  ..$ ID1      : chr [1:41] "A1" "A2" "A3" "A4" ...
  ..$ ID2      : chr [1:41] "A2" "A3" "A4" "A5" ...
  ..$ Value    : num [1:41] 422000 400000 320000 320000 300000 280000 240000 210000 180000 175000 ...
  ..$ direction: Factor w/ 2 levels "A","R": 1 1 1 1 1 1 1 1 1 1 ...
 $ nodes :'data.frame':	39 obs. of  3 variables:
  ..$ ID       : chr [1:39] "A1" "A2" "A3" "A4" ...
  ..$ Longitude: num [1:39] 24 24.5 25.5 26 27 28 28.5 29 30 30.3 ...
  ..$ Latitude : num [1:39] 54.9 55 54.5 54.7 54.8 54.9 55 55.1 55.2 55.3 ...
 $ cities:'data.frame':	18 obs. of  3 variables:
  ..$ Longitude: num [1:18] 24 25.3 26.4 26.8 27.7 28.5 28.7 29.2 30.2 30.4 ...
  ..$ Latitude : num [1:18] 55 54.7 54.4 54.3 55.2 54.3 55.5 54.4 55.3 54.5 ...
  ..$ Name     : Factor w/ 20 levels "Bobr","Chjat",..: 5 18 15 9 4 16 13 1 19 12 

 

 

 

 

R riverplot package를 사용해서 드디어 Sankey diagram을 그려보겠습니다.  R script는 riverplot package(* author : January Weiner)에 있는 예제 script를 그대로 인용하였습니다. (날로 먹는 듯한 이 기분..^^;) 

 

 

> ############################
> # Sankey diagram 
> # using R riverplot package 
> # minard data (list format)
> ############################
> 
> # install.packages("riverplot")
> library(riverplot)
> data( minard )
> 
> nodes <- minard$nodes
> edges <- minard$edges
> colnames( nodes ) <- c( "ID", "x", "y" )
> colnames( edges ) <- c( "N1", "N2", "Value", "direction" )
> 
> 
> # color the edges by troop movement direction
> edges$col <- c( "#e5cbaa", "black" )[ factor( edges$direction ) ]
> 
> # color edges by their color rather than by gradient between the nodes
> edges$edgecol <- "col"
> 
> # generate the riverplot object and a style
> river <- makeRiver( nodes, edges )
> style <- list( edgestyle= "straight", nodestyle= "invisible" )
> 
> # plot the generated object
> plot( river, lty= 1, default_style= style )
> 
> # Add cities
> with( minard$cities, points( Longitude, Latitude, pch= 19 ) )
> with( minard$cities, text( Longitude, Latitude, Name, adj= c( 0, 0 ) ) )
> # Add title
> title("Sankey Diagram - Napoleon army march, minard")

 

* R script author : January Weiner

 

 

다음번 포스팅에서는 igraph package를 사용해서 방향성 있는 가중 네트워크를 시각화해보겠습니다.

 

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

 

[Reference]

 - R riverplot package manual : https://cran.r-project.org/web/packages/riverplot/riverplot.pdf

 - On minards visualization : https://robots.thoughtbot.com/analyzing-minards-visualization-of-napoleons-1812-march

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

폴더에 자잘하게 쪼개진 여러개의 파일들이 있을 때, 그리고 이 파일들을 일일이 R로 불러오기 해야 할 때, 더그리고 이들 불러온 파일을 한개의 데이터셋을 합쳐야 할 때 (이쪽 동네 전문용어로) 노가다를 하지 않고 좀더 스마트하게 하는 방법을 소개하겠습니다.

 

순서는 다음과 같습니다.

  • (1) 폴더 경로 객체로 만들기
  • (2) 폴더 내 파일들 이름을 list-up 하여 객체로 만들기
  • (3) 파일 개수 객체로 만들기
  • (4) 폴더 내 파일들을 LOOP 돌려서 불러오기 : read.table()
  • (5) 파일을 내보내면서 합치기 : write.table(dataset, APPEND = TRUE)
  • (6) 데이터프레임으로 불러오기, 칼럼 이름 넣기 : read.table(dataset_all, col.names = c())

 

자, 예를 들면서 순서대로 R script 설명하겠습니다.

 

 

아래의 화면캡쳐 예시처럼 MyDocuments > R > FILES 폴더 아래에 daily로 쪼개진 10개의 text 파일들이 들어있다고 해봅시다.  (10개 정도야 일일이 불어올 수도 있겠지만, 100개, 1,000개 파일이 들어있다면?)

 

 

 

 

  • (1) 폴더 경로 객체로 만들기

> # cleaning up environment > rm(list=ls()) >

> # making directory as an object > src_dir <- c("C:/Users/Owner/Documents/R/FILES") # 경로 구분 : '\'를 '/'로 바꿔야 함 >
>
src_dir [1] "C:/Users/Owner/Documents/R/FILES" 

 

 

  • (2) 폴더 내 파일들 이름을 list-up 하여 객체로 만들기 : list.files()

> # listing up name of files in the directory => object
>

>
src_file <- list.files(src_dir) # list
>
> src_file [1] "day_20160701.txt" "day_20160702.txt" "day_20160703.txt" "day_20160704.txt" [5] "day_20160705.txt" "day_20160706.txt" "day_20160707.txt" "day_20160708.txt" [9] "day_20160709.txt" "day_20160710.txt"

 

 

 

"C:/Users/Owner/Documents/R/FILES" 디렉토리에 들어있는 파일들을 열어보면 아래와 같은 데이터들이 들어있습니다. (가상으로 만들어 본 것임)  daily로 집계한 데이터들이 들어있네요.

 

 

  • (3) 파일 개수 객체로 만들기 : length(list)

 

> # counting number of files in the directory => object
> 
> src_file_cnt <- length(src_file) >
> src_file_cnt [1] 10

 

 

 

여기까지 R을 실행하면 아래와 같이 environment 창에 객체들이 생겼음을 확인할 수 있습니다.

 

 


 

  • (4) 폴더 내 파일들을 LOOP 돌려서 불러오기
    => (5) 파일을 내보내면서 합치기 : write.table(dataset, APPEND = TRUE)


    : for(i in 1:src_file_cnt) {read.table()
                                     write.table(dataset, append = TRUE)}

 

> # write.table one by one automatiically, using loop program > for(i in 1:src_file_cnt) { + + # write.table one by one automatiically, using loop program + dataset <- read.table( + paste(src_dir, "/", src_file[i], sep=""), + sep=",", header=F, stringsAsFactors = F) + + # dataset exporting with 'APPEND = TREU' option, filename = dataset_all.txt + write.table(dataset, + paste(src_dir, "/", "dataset_all.txt", sep=""), + sep = ",", + row.names = FALSE, + col.names = FALSE, + quote = FALSE, + append = TRUE) # appending dataset (stacking) + + # delete seperate datasets + rm(dataset) + + # printing loop sequence at console to check loop status + print(i) + } [1] 1 [1] 2 [1] 3 [1] 4 [1] 5 [1] 6 [1] 7 [1] 8 [1] 9 [1] 10

 

 

 

여기까지 실행을 하면 아래처럼 MyDocuments>R>FILES 폴더 아래에 'dataset_all.txt' 라는 새로운 텍스트 파일이 하나 생겼음을 확인할 수 있습니다. 

 


 

 

새로 생긴 'dataset_all.txt' 파일을 클릭해서 열어보면 아래와 같이 'day_20160701.txt' ~ 'day_20160710.txt'까지 10개 파일에 흩어져있던 데이터들이 차곡차곡 쌓여서 합쳐져 있음을 확인할 수 있습니다.

 

 

 

 

  • (6) 데이터 프레임으로 불러오기 : read.table()
         칼럼 이름 붙이기 : col.names = c("var1", "var2", ...)

> # reading dataset_all with column names
> dataset_all_df <- read.table(
+   paste(src_dir, "/", "dataset_all.txt", sep=""), 
+   sep = ",", 
+   header = FALSE, # no column name in the dataset
+   col.names = c("ymd", "var1", "var2", "var3", "var4", "var5", 
+                 "var6", "var7", "var8", "var9", "var10"), # input column names
+   stringsAsFactor = FALSE, 
+   na.strings = "NA") # missing value : "NA"

 

 

 

우측 상단의 environment 창에서 'dataset_all_df' 데이터 프레임이 새로 생겼습니다.

클릭해서 열어보면 아래와 같이 'day_20160701.txt' ~ 'day_20160710.txt'까지 데이터셋이 합쳐져있고, "ymd", "var1" ~ "var10" 까지 칼럼 이름도 생겼습니다.

 

 

 

프로그래밍을 통한 자동화가 중요한 이유, 우리의 시간은 소중하니깐요~! ^^

 

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

 

 

====================================================================

(2018.03.14일 내용 추가)

 

댓글 질문에 '폴더에 있는 개별 파일을 하나씩 읽어와서 하나씩 DataFrame 객체로 메모리상에 생성하는 방법에 대한 질문이 있어서 코드 추가해서 올립니다. 위에서 소개한 방법과 전반부는 동일하구요, 마지막에 루프 돌릴 때 assign() 함수로 파일 이름을 할당하는 부분만 조금 다릅니다.

 

 

#=========================================================
# read all files in a folder and make a separate dataframe
#=========================================================

rm(list=ls()) # clear all

 

# (1) directory
src_dir <- c("D:/admin/Documents/R/R_Blog/326_read_all_files")

 

# (2) make a file list of all files in the folder
src_file <- list.files(src_dir)
src_file

 

 

# (3) count the number of files in the directory => object
src_file_cnt <- length(src_file)
src_file_cnt # 5

 

# (4) read files one by one using looping
#     => make a dataframe one by one using assign function
for (i in 1:src_file_cnt){
  assign(paste0("day_", i), 
         read.table(paste0(src_dir, "/", src_file[i]),
                    sep = ",",
                    header = FALSE))
  print(i) # check progress
}

 

rm(src_dir, src_file, src_file_cnt, i) # delete temp objects
ls() # list-up all dataframes

 

 

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 이전 댓글 더보기
  2. cnk 2018.09.10 19:16  댓글주소  수정/삭제  댓글쓰기

    좋은 자료 감사합니다.

    10개의 파일을 loop를 통해 assign함수를 이용하여 원하는 정보를 읽어서 Environment에 불러 읽는 작업까지 진행하였습니다. 혹시 읽은 파일들을 output file (csv or txt)형식으로 각각 읽은 10개의 파일을 10개의 output으로 만들고 싶은데 어떤식으로 진행해야 할 지 모르겠습니다.

    도움을 주신다면 감사하겠습니다.

    • R Friend R_Friend 2018.09.11 15:08 신고  댓글주소  수정/삭제

      안녕하세요. cnk님,

      (1) ls(pattern = "data_") 함수를 이용하면 R Environment 상에 있는 객체 중에서 특정 패턴을 가진 객체를 리스트업하여 새로운 객체로 만들어주세요. (자세한 내용은 http://rfriend.tistory.com/350 참조)

      (2) For loop 반복문을 사용해서 write.table() 함수를 이용하여 (1)번에서 찾은 여러개의 데이터프레임을 loop를 돌면서 하나씩 text 파일로 내보내기를 해주면 됩니다. paste() 함수를 사용해서 파일 이름이 계속 바뀌게 해주면 됩니다. (write.table() 함수의 자세한 내용은 http://rfriend.tistory.com/17 참조)

  3. 2018.09.11 15:18  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  4. JHJH 2019.01.02 13:35  댓글주소  수정/삭제  댓글쓰기

    위의 방법처럼 폴더 안의 파일들을 NGS_,i 이름의 CSV로 불러온 뒤 각 NGS_i 파일을 for 루프를 이용해서 grep을 해오려고 하는데 잘되지 않네요 ㅠㅠ

    mutation.txt에는 유전자의 이름이 있고, 이를 collapse로 ^,$를 붙혀 exact matching을 하는 방식입니다.
    일일히 grep을 쓰기 너무 힘들어서 루프로 짜보고 있는데 어렵네요...

    gene <-read.table("C:/JHR/pancchr/mutation.txt", header=T, sep="\t")
    v.gene <-as.vector(t(gene))

    for (i in 1:src_file_lnc){
    assign(paste0("list_",i),
    grep(paste(v.gene,collapse = "$|^"),paste0("NGS_",i)[12]))
    print(i)
    }

    이렇게 했을때 list_1을 불러오면 NA가 되버리네요ㅠ 뭐가 문제일까요?

    assign, paste0도 같이 공부해봐야겠습니다ㅠ

    • R Friend R_Friend 2019.01.02 14:25 신고  댓글주소  수정/삭제

      안녕하세요 JH님. 반갑습니다.
      제가 데이터를 가지고 있지 않아서 아래 코드가 정확하게 작동하는지 확인은 할 수가 없는데요, 우선 원하는 결과물에 대해 설명해주신 내용을 토대로 코드를 일부 수정해보았습니다.

      설명해주신 내용 중에 "폴더 안의 파일들을 NGS_,i 이름의 CSV로 불러온 뒤" 라는 부분에 대한 코드가 없는 것처럼 보여서요, 그 부분을 for loop 문 안에 추가를 하였습니다.

      NGS_i 파일들만 특정 폴더에 모두 들어있다고 가정하였습니다.

      댓글란에 코드를 복사해서 넣으면 들여쓰기가 모두 없어지므로 읽기에 좀 어려운데요, R Studio에 코드 복사해서 사용해보시기 바랍니다.

      # (1) directory
      src_dir <- c("D:/admin/Documents/YourDirectory")

      # (2) make a file list of all files in the folder
      src_file <- list.files(src_dir)
      src_file

      # (3) count the number of files in the directory => object
      src_file_cnt <- length(src_file)
      src_file_cnt

      # (4) read files one by one using looping
      # => make a dataframe one by one using assign function
      for (i in 1:src_file_cnt){
      # read NGS files first and make it as a DataFrame
      assign(paste0("NGS_", i),
      read.table(paste0(src_dir, "/", src_file[i]),
      sep = ",",
      header = FALSE))
      # exact matching by using grep
      assign(paste0("list_", i),
      grep(paste(v.gene, collapse = "S|^"), paste0("NGS_", i)[12]))
      print(i) # check progress
      }

  5. JHJH 2019.01.02 20:47  댓글주소  수정/삭제  댓글쓰기

    오 친절한 답변 너무 감사합니다. 한 번에 루프를 넣는 방법이 있었군요ㅠㅠ

    for (i in 1:src_file_lnc){
    >>>assign(paste0("NGS_",i),
    >>>>>>read.csv(paste0(src_dir, "/", src_file[i]),
    >>>>>>stringsAsFactors = T,
    >>>>>>na.strings = T,
    >>>>>>header = T))
    >>>print(i)
    }

    제가 csv로 불러오고 헤더와 stringasfactor를 필요로 해서 이렇게 넣어봣는데 read.table로 하는 것과는 어떤 차이가 있는지 알 수 있을까요??
    NGS_i 파일의 1번쨰 column을 불러오기 위해서[12]를 넣었는데 결과물인 list_i 의 파일들이 NA로 나오는걸 보니 사용하는 함수를 바꾸어보아야할 것 같습니다 조언 감사합니다!!

  6. Yu 2019.01.11 17:50  댓글주소  수정/삭제  댓글쓰기

    안녕하세요.
    덕분에 작업에 시간단축이 굉장히 많이 되었습니다.

    그러나, 알려주신 코드로 진행을 하는데 오류가 조금 있어서..

    (5) for문까지 진행을 하면

    Error in type.convert.default(data[[i]], as.is = as.is[i], dec = dec, : invalid multibyte string at '<ed><96><89>?뺤옄移섏쐞<ec>썝<ed>쉶<ed>쉶<ec>쓽濡<9d>'

    위와 같은 에러코드가 발생합니다.

    컴퓨터 상에 txt파일을 열면 한글이 깨지지 않고 잘 보이는데.. 문제가 무엇인지 모르겠습니다.

    답변 가능하시다면 부탁드리도록 하겠습니다.

    좋은 하루보내세요.

    • R Friend R_Friend 2019.01.11 21:00 신고  댓글주소  수정/삭제

      안녕하세요.

      아무래도 encoding 문제 때문에 에러가 발생한 것 같습니다.
      ( 참고: http://rfriend.tistory.com/10 )

      read.table 로
      read.table("filename.csv", sep=",", header=FALSE, stringsAsFactors=FALSE, fileEncoding="UTF-8")

      혹은,

      read.table("filename.csv", sep=",", header=FALSE, stringsAsFactors=FALSE, fileEncoding="CP949")

      를 한번 시도해보시면 좋겠습니다.

  7. Yu 2019.01.14 10:19  댓글주소  수정/삭제  댓글쓰기

    안녕하십니까.
    이전에 문의 드렸던 인코딩 관련해서 한번 더 문의를 드리겠습니다.
    말씀해주신 방법으로 (fileEncoding="UTF-8"도 시도해보았음)

    for(i in 1:src_file_cnt){
    dataset <- read.table(
    paste(src_dir, "/", src_file[i], sep=""),
    sep=",", header=F, stringsAsFactors = F, fileEncoding="CP949")
    write.table(dataset,
    paste(src_dir, "/", "dataset_all.txt", sep=""),
    sep=",",
    row.names = F,
    col.names = F,
    quote = F,
    append)
    }

    위의 코드를 입력해도

    Error in as.logical(test) :
    cannot coerce type 'closure' to vector of type 'logical'
    In addition: Warning messages:
    1: In read.table(paste(src_dir, "/", src_file[i], sep = ""), sep = ",", : invalid input found on input connection 'C:/Users/CNI/Desktop/YMS/?낅Т/20190107_?뚯쓽濡?9?€/?곸엫?꾩썝???됱젙?먯튂?꾩썝???됱젙?먯튂?꾩썝??txt/-Unlicensed-235-?됱젙?먯튂1李?txt'
    2: In read.table(paste(src_dir, "/", src_file[i], sep = ""), sep = ",", : 'C:/Users/CNI/Desktop/YMS/?낅Т/20190107_?뚯쓽濡?9?€/?곸엫?꾩썝???됱젙?먯튂?꾩썝???됱젙?먯튂?꾩썝??txt/-Unlicensed-235-?됱젙?먯튂1李?txt'에서 readTableHeader에 의하여 발견된 완성되지 않은 마지막 라인입니다

    위와 같은 에러가 발생합니다.

    for문으로 텍스트를 불러 통합된 데이터 저장까지를 진행하고싶은데.. 문제가 무엇인지 모르겠습니다.

    그러나 for문을 사용하지 않고 개별로

    a01 <- readLines("filename.txt", encoding="UTF-8")

    위와 같은 코드를 실행할때는 한글이 깨지지 않고 잘 불러와집니다.

    혹시 폴더안에 있는 파일을 모두 readLine으로 불러 하나의 txt파일로 저장하는 for문을 제공해주실 수 있으실까요..

    죄송하지만 부탁드리겠습니다. 좋은하루보내세요.

    • R Friend R_Friend 2019.01.14 12:13 신고  댓글주소  수정/삭제

      안녕하세요 Yu님,

      남겨주신 코드에 보니 append 만 쓰셨고 append = TRUE 라고 명시적으로 TRUE를 안쓰셨는데요, TRUE 포함해서 시도해보실래요?

      아래 코드는 readLines()를 사용하고, blank DataFrame을 하나 만들어놓고 거기에 메모리 상에서 하나씩 파일을 읽어들여 만든 DataFrame Temp를 세로로 쌓은(stack) 후에 => 마지막에 data_all DataFrame을 export 하는 코드 입니다. 한번 시도해보시고 잘 안되면 댓글 남겨주세요.

      src_dir <- c("YourDirectoryHere")
      src_file <- list.files(src_dir)
      src_file_cnt <- length(src_file)

      data_all <- data.frame() # blank df for stacking later

      for (i in 1:src_file_cnt){
      # read text file
      dataset_tmp <- readLines(paste0(src_dir, "/", src_file[i]),
      encoding="UTF-8")

      # stack it to data_all
      data_all <- rbind(data_all, dataset_tmp) # assume that columns are the same

      print(i, " th file is done") # progress check
      }

      # export data_all DataFrame into text file
      write.table(data_all, paste0(src_dir, "/", data_all.txt))

  8. Yu 2019.01.14 13:28  댓글주소  수정/삭제  댓글쓰기

    안녕하세요.
    우선 빠른 답변 너무나도 감사드립니다.

    첫번째로 append = TRUE 를 포함한 코드에서도 동일한 에러가 발생합니다.
    아마 append = TRUE는 입력을 하였는데 질문을 드릴때 댓글을 작성하다가 지워진 것 같습니다.

    두번째로 제공해주신 코드도 에러가 발생하였습니다.

    src_dir <- c("YourDirectoryHere")
    src_file <- list.files(src_dir)
    src_file_cnt <- length(src_file)

    data_all <- data.frame() # blank df for stacking later

    위 코드 까지는 에러없이 잘 진행이 되었습니다 (참고_ src_file_cnt = 92L 입니다.)

    그러나 for문에 있어서는..

    Error in print.default(i, " th file is done") : invalid 'digits' argument
    In addition: Warning message:
    In print.default(i, " th file is done") : NAs introduced by coercion

    위와 같은 에러코드가 발생하였습니다.

    그 뒤에
    write.table(data_all, paste0(src_dir, "/", data_all.txt))

    위 코드 또한

    Error in paste0(src_dir, "/", data_all.txt) :
    object 'data_all.txt' not found

    위와 같은 에러가 발생하였습니다.

    바쁘실텐데 번거롭게 정말 죄송합니다.

    무엇이 문제인지 전혀 감이 오지 않습니다..ㅠㅠ

    가능하시다면 한번 더 설명을 부탁드리겠습니다.

    참고로 제가 불러오고자 하는 텍스트 파일은 csv나 구분자가 따로 없는 회의록입니다.

    개별 회의록을 모두 불러와 한개의 회의록 데이터를 만들고자 하는 작업입니다.

    답변 부탁드리겠습니다. 늘 감사합니다.

    • R Friend R_Friend 2019.01.14 13:39 신고  댓글주소  수정/삭제

      수정한 코드인데요, 한번 해보실래요?
      rbind()를 쓰려면 칼럼 숫자랑 이름, 데이터 형태가 같아야 하는데요, 회의록이라면 rbind()가 잘 작동할지 불확실하네요.

      src_dir <- c("YourDirectoryHere")
      src_file <- list.files(src_dir)
      src_file_cnt <- length(src_file)

      data_all <- data.frame() # blank df for stacking later

      for (i in 1:src_file_cnt){
      # read text file
      dataset_tmp <- readLines(paste0(src_dir, "/", src_file[i]),
      encoding="UTF-8")

      # stack it to data_all
      data_all <- rbind(data_all, dataset_tmp) # assume that columns are the same

      rm(dataset_tmp) # remove temp df

      print(paste0(i, " th file is done")) # progress check
      }

      # export data_all DataFrame into text file
      write.table(data_all, paste0(src_dir, "/", "data_all.txt"))

  9. 2019.01.14 13:58  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  10. Yu 2019.01.14 14:10  댓글주소  수정/삭제  댓글쓰기

    비밀댓글 확인 가능하신가요?ㅠㅠ

    • R Friend R_Friend 2019.01.14 14:17 신고  댓글주소  수정/삭제

      아래 코드로 실행해보시지요.

      src_dir <- c("YourDirectoryHere")
      src_file <- list.files(src_dir)
      src_file_cnt <- length(src_file)

      for (i in 1:src_file_cnt){
      # read text file
      dataset <- readLines(paste0(src_dir, "/", src_file[i]),
      encoding="UTF-8")
      # export & append
      write.table(dataset,
      paste(src_dir, "/", "dataset_all.txt", sep=""),
      sep=",",
      row.names = F,
      col.names = F,
      quote = F,
      append = TRUE)

      print(paste0(i, " th file is done")) # progress check
      }

  11. Yu 2019.01.14 14:25  댓글주소  수정/삭제  댓글쓰기

    헐대박

    보내주신 코드로 진행하니 개별 파일들이 모두 불러져 하나의 데이터로 통합이 되었습니다.
    정말 너무 감사드립니다.

    ㅠㅠㅠㅠㅠ어떻게 감사의 말씀을 드려야할지 모르겠네요 정말 감사합니다.

    앞으로도 자주 방문하여 많은 공부할 수 있도록 하겠습니다.

    친절한 답변 정말 감사합니다.

  12. 2019.01.22 16:04  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.02.08 12:02 신고  댓글주소  수정/삭제

      안녕하세요.
      아래 코드를 참고해서 실행해보시기 바랍니다.

      #========================================================================
      # Read lots of files and then export it word by word with comma separator
      #========================================================================
      rm(list=ls()) # clear all

      #---------- (0) Prerequisite :
      # I've removed all chinese from the file name due to some error during read.table()


      #----------
      # (1) Read lots of files at the same folder using for loop

      # make a source file list
      src_dir <- c("C:/Users/admin/Downloads/test_office_0123") # set with yours
      src_file <- list.files(src_dir)

      # destination directory
      dst_dir <- c("C:/Users/admin/Downloads") # set with yours
      dst_file_name <- c("sillok_data.txt") # set with yours


      # read many files and export it line by line by looping operation
      for (i in 1:length(src_file)){
      # read a file
      df_tmp <- read.table(paste0(src_dir, "/", src_file[i]),
      sep = ",",
      header = FALSE,
      stringsAsFactors = FALSE)

      name <- strsplit(df_tmp[2,], split = " ")[[1]][2] # name

      for (j in 1:nrow(df_tmp)){
      # split a string by space
      splitted_string <- data.frame(do.call('rbind',
      strsplit(df_tmp[j,],
      split = " ",
      fixed = TRUE)))

      # combining i, j, name, and strings of a line
      df_string <- data.frame(i, j, name, splitted_string)

      # export a file and append it line by line
      write.table(df_string,
      paste0(dst_dir, "/", dst_file_name),
      sep = ",",
      row.names = FALSE,
      col.names = FALSE,
      quote = FALSE,
      append = TRUE)
      }

      print(paste0(i, "/", length(src_file), " is done!")) # to check progress
      }

      rm(df_tmp, df_string, splitted_string, i, j, name) # remove temp file

  13. 2019.08.16 19:37  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  14. R Friend R_Friend 2019.08.16 19:46 신고  댓글주소  수정/삭제  댓글쓰기

    data를 한개씩 불러올때 마다 original data에 merge를 하는 것으로 for loop을 사용햐보세요.

    merge는 한번에 Dataframe 한개씩만 가능해서요, 댓글에 남겨주신 것처럼 여러개의 data를 한꺼번에 merge하려고 하면 에러가 날거예요.

    • 알초보 2019.08.16 20:55  댓글주소  수정/삭제

      댓글 감사합니다ㅠㅠ
      제가 알완전 초보라서요..ㅠㅠ
      혹시 어떻게 코드를 작성하면되질 알수있을까요?
      이 데이터가 1000개인데 오늘 하루종일 엑셀 브이룩으로 찾아도 100개를 못해서요ㅠㅠ

    • R Friend R_Friend 2019.08.16 21:02 신고  댓글주소  수정/삭제

      아래 링크에 R로 엑셀파일 읽어오는 패키지, 코드 참고해보세요.

      https://rfriend.tistory.com/m/313

      나머지 for loop은 블로그 본문 참고하시구요.

  15. 2019.08.17 01:46  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.08.17 09:34 신고  댓글주소  수정/삭제

      merge할때 아래처럼 살짝 바꿔서 해보실래요? left outer join (original data 기준으로 merge) 하려는거지요?

      original_data <- merge(original_data, ReadInMerge, by="Phone", all.x=T)

      merge() 함수는 아래 링크 참조하세요.
      https://rfriend.tistory.com/m/51

    • 알초보 2019.08.17 14:06  댓글주소  수정/삭제

      댓글감사합니다!
      말씀해주신 방향대로 해도
      마지막꺼 하나만 남네요ㅠㅠ
      누적으로 각 csv당 하나씩 한 열로 추가가 불가능한가요,,,

    • R Friend R_Friend 2019.08.19 09:07 신고  댓글주소  수정/삭제

      혹시 dataMerge <- merge(xxxxx) 로 하지 않으셨나요? 병합이 된 결과 객체를 original_data로 바꾸어서 해보세요. original_data에 계속 붙이는 형식으로요.

      original_data <- merge(original_data, ReadInMerge, by="Phone", all.x=T)

  16. 2019.09.03 19:38  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.09.03 21:53 신고  댓글주소  수정/삭제

      안녕하세요.
      지금 문제 상황이 정확하게 어떤 것이지 잘 모르겠습니다.

      R, text parsing, regular expression 등의 키워드를 같이 넣어서 구글링 해보시면 좋겠습니다. readLines() 로 텍스트 읽어서 regular expression 을 목적에 맞게 찾아서 작성하시면 숫자만 선별적으로 골라올 수 있을 거예요.

  17. 2019.10.15 09:55  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.10.15 09:59 신고  댓글주소  수정/삭제

      안녕하세요.

      데이터 사이즈가 크몀 DB에서 SQL로 처리하는 것을 추천드립니다.

      데이터를 어떻게 생겼는지 볼 수 없으므로 R코두드에 대해서 제가 별로 말씀드릴 수 있는 것이 없습니다. 죄송합니다.

  18. 2019.10.16 16:05  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  19. OGU 2019.11.11 15:23  댓글주소  수정/삭제  댓글쓰기

    올려주신 내용 너무 감사합니다!

    그런데

    for (i in 001:cnt){assign(paste0("nc_", i), read.table(paste0(road, "/", list[i]),sep = ",", header = FALSE ))}


    다음과 같은 코드로 데이터를 불러왔습니다.

    그런데 왜 nc_1,nc_2 이런 순서가 아니라 nc_1, nc10, nc_100 ..같은 순서로 불러옵니다 ㅠㅠ
    순서대로 불러오는 방법이 있을까요?

    • R Friend R_Friend 2019.11.11 15:25 신고  댓글주소  수정/삭제

      "_001", "_002", "_003" 이런식으로 네이밍할 수 있으면 순서대로 불러올수 있을것 같습니다.

    • OGU 2019.11.11 16:57  댓글주소  수정/삭제

      for (i in 001:cnt) 처럼 001부터 순서를 부여했음에도 불구하고 1,10,100,,, 같은 순서를 부여합니다 ㅠㅠ

    • R Friend R_Friend 2019.11.11 17:02 신고  댓글주소  수정/삭제

      cnt 를 정의한 코드를 볼 수 있을까요? for loop문이 작동하는 방식을 봐야할거 같아요.

    • OGU 2019.11.11 17:07  댓글주소  수정/삭제

      road<-c("C:/Users/go/Desktop/Data/r1999/0102/nc")

      list.files(road)->list

      cnt <- length(list)

      다음과 같은 코드입니다!

    • R Friend R_Friend 2019.11.11 17:10 신고  댓글주소  수정/삭제

      퇴근 후에 찬찬히 살펴볼께요. 제가 지금 업무중이라서요.

    • OGU 2019.11.11 19:35  댓글주소  수정/삭제

      네^~^ 감사합니다

    • R Friend R_Friend 2019.11.12 01:03 신고  댓글주소  수정/삭제

      안녕하세요 OGU님,

      for (i in 1:cnt) 부분은 문제가 없습니다. 따라서 assign(paste0("nc_", i) 이 부분도 문제가 없습니다.

      list <- list.files(road) 로 road 폴더 안의 파일 리스트를 만들어 놓은 list 객체에 아마도 1, 10, 100, ... 의 순서로 파일 이름이 리스트에 저장이 된 것 같습니다. 그러다보니 list[i] 로 인덱스해서 파일 이름을 가져올 때 1, 10, 100, ... 의 순서로 파일을 가져오는 것 같습니다.

      print(list) 를 해서 list 안의 파일 이름 순서가 혹시 1, 10, 100, ... 이런 순서로 되어있는게 아닌지 확인해보시기 바랍니다.

      혹시 이게 문제라면요, 원본 파일의 넘버링 구분자(끝부분?)를 "_001.txt", "_002.txt" 이런식으로 고쳐서 한번 해보실래요?

    • OGU 2019.11.12 11:08  댓글주소  수정/삭제

      확인해주셔서 감사합니다!
      하지만 원본파일이름은 001, 002, 003.. 과 같은순서로 되어있습니다 ㅠㅠ


      1. road<-c("C:/Users/go/Desktop/r1999/0102/nc")
      2. list.files(road)->list
      3. cnt <- length(list)

      4. for (i in 01:cnt){assign(paste0("nc_", i), read.table(paste0(road, "/", list[i]),sep = ",", header = FALSE ))}

      4번과정에서 데이터를 불러오면 Environment 창에 1,10,100,101,, 같은 순서로 불러집니다.


      https://www.google.com/search?q=why+r+order+files+as+1+10+100&oq=why+r+&aqs=chrome.0.69i59j69i57j69i60l3.4743j0j7&sourceid=chrome&ie=UTF-8

      여기서 첫번째 게시물과 같은 문제인것 같아요 ㅠㅠ

    • R Friend R_Friend 2019.11.12 11:57 신고  댓글주소  수정/삭제

      아, 그럼 버그인건가요? ㅠ.ㅠ

    • OGU 2019.11.14 14:42  댓글주소  수정/삭제

      앗,,ㅠㅠ 그런가요ㅠㅠ 도움주셔서 감사합니다~ㅎㅎ

  20. JHK 2019.11.30 04:59  댓글주소  수정/삭제  댓글쓰기

    안녕하세요~ 저는 파일이 처음부터 6개까지는 변수가 12개인데 7번째 파일 부터는 변수 한개가 더 추가되어있는 상태라서요! 열 수가 맞지 않는다라는 에러가 뜹니다.
    7번째 파일부터 추가되어있는 변수는 지우고 합치고 싶은데 어떻게 하면 될까요>

군집 간 거리를 측정하는 방법에 따라서 여러가지 알고리즘이 있는데요, 지난번 포스팅에서는 응집형 계층적 군집화(agglomerative hierarchical clustering) 알고리즘 중에서 그래프 기반(Graph-based)의 (1-1) 단일(최단) 연결법 (single linkage method, MIN), (1-2) 완전(최장) 연결법 (complete linkage method, MAX), (1-3) 평균 연결법 (average linkage method) 에 대해 소개하였습니다.

 

 

 

응집형 계층적 군집화 알고리즘 중에서 프로토타입 기반(Prototype-based) 모형은 미리 정해놓은 각 군집의 프로토타입에 터이터가 얼마나 가까운가로 군집의 형태를 결정합니다. 프로토타입 기반 유사성 측도로서 두가지 방법인 (1-4) 중심 연결법 (centroid linkage method)과 (1-5) Ward 연결법 (Ward linkage method) 중에서 이번 포스팅에서는 (1-4) 중심 연결법에 대해서 소개하겠습니다.

 

 

 

 

중심 연결법(Centroid Linkage method)은 두 군집 간의 거리를 측정할 때 각 군집의 중심(centroid) 간의 거리를 사용합니다.  아래 그림의 왼쪽의 이미지를 참고하시기 바랍니다.

 

     

[ 프로토타입 기반 유사성 측정 (Prototype-based maesure of proximity) ]

 

[표기 (denotation) 설명]

- d(i+j, k) : 군집 i와 j가 합쳐진 새로운 군집 i+j (cluster i U j)와 군집 k 간의 거리(distance)

- μi+j : 군집 i와 r군집 j의 데이터를 가중평균(weighted average)을 이용해 계산한 새로운 중심
- ni : 군집 i의 데이터 개수,  nj : 군집 j의 데이터 개수

        

- 빨간점 : 각 군집의 중심(centroid)

 

 

 

이제 2차원 공간(2-dimentional space)에 5개의 점을 가지고 간단한 예를 들어서 설명을 해보겠습니다.

 

지난번 포스팅의 단일(최단) 연결법, 완전(최장) 연결법, 평균 연결법과 예시 데이터와 동일하며, 거리 계산 방법도 아래의 (1)번, (2)번까지는 똑같고, (3)번부터는 조금 다릅니다. 

 

 

 

1) 데이터셋 준비, 탐색적 분석

 

응집형 계층적 군집화이므로 처음에 아래의 5개의 점, 5개의 군집에서부터 시작합니다.

 

 

 

R script도 같이 제시하면서 설명하겠습니다.  먼저, 데이터 입력 및 plotting (↑ 위의 산점도 그래프) 입니다.

 

> ##--------------------------------------------
> ## (1) Agglomerative Hierarchical Clustering 
> ##   (b) Prototype-based
> ##    (1-4) Centroid Linkage
> ##--------------------------------------------
> 
> x <- c(1, 2, 2, 4, 5)
> y <- c(1, 1, 4, 3, 4)
> 
> xy <- data.frame(cbind(x, y))
> 
> xy
  x y
1 1 1
2 2 1
3 2 4
4 4 3
5 5 4
> 
> # scatter plot of xy
> plot(xy, pch = 19, xlab = c("x coordinate"), ylab = c("y coordinate"), 
+      xlim = c(0, 6), ylim = c(0, 6), 
+      main = "scatter plot of xy")
> 
> # adding student label
> text(xy[,1], xy[,2], labels = abbreviate(rownames(xy)), 
+      cex = 0.8, pos = 1, col = "blue") # pos=1 : at the bottom
> 
> 
> # adding dotted line
> abline(v=c(3), col = "gray", lty = 2) # vertical line
> abline(h=c(3), col = "gray", lty = 2) # horizontal line

 

 

 

 

 

2) 유사성 측도로서 거리 행렬(Distance matrix) D 계산하기

 

거리 측도는 분석 목적, 데이터 특성에 맞게 선택해야 하는데요, 이번 예제에서는 '유클리드 제곱거리(squares of Euclidean distance)'를 사용하겠습니다. 

 

[distance matrix - no.1]

 

 

 

유클리드 제곱거리를 구하는 R script 입니다. dist(xy, method="euclidean") 에다가 뒤에 "^2"를 붙여서 제곱을 했습니다.

 

> # proximity matrix : squares of euclidean distance matrix for 6 points
> dist(xy, method = "euclidean")^2
   1  2  3  4
2  1         
3 10  9      
4 13  8  5   
5 25 18  9  2

 

 

 

  • P1과 P2의 거리가 '1'로서 가장 가까우므로 (즉, 유사하므로) 
    → (P1, P2)를 새로운 군집으로 묶어줍니다(merge). 이제 군집이 처음 5개에서 4개로 줄었습니다.

 

2차원 데이터에 대해서는 아래처럼 부분집합그림(Nested cluster diagram)을 그려볼 수 있습니다.

 

 

 

(여기까지는 단일 연결법, 완전 연결법, 평균 연결법과 동일합니다)

 

 

 

3) 군집(P1, P2)의 중심 구하기

 

새로 묶인 군집(P1, P2)의 중심(centroid)을 가중평균을 이용해서 구해보면

μ(P1+P2) = {1*(1, 1) + 1*(2, 1)}/(1+1) = {(1, 1) + (2, 1)}/2 = (1.5, 1) 이 됩니다.

 

여기서 부터 앞서 소개했던 그래프 기반(Graph-based)의 군집 간 거리측정법인 (1-1) 단일 연결법, (1-2) 완전 연결법, (1-3) 평균 연결법과 확연히 차이가 나기 시작하는 부분입니다.  그래프 기반 방법에서는 중심(Centroid)라는 개념이 없었구요, 프로토타입 기반 방법 중에서 중심 연결법에서는 프로토타입으로 군집의 중심(Centroid)을 가지고 군집 간 거리를 측정합니다.

 

[centroid coordinate of clusters - no.1]

 

 

 

아래의 부분집합그림에 보면 군집 (P1, P2) 의 중심(centroid) 위치에 노란색 별이 추가되었습니다.

 

 

 

 

4) 군집(P1, P2)와 P3, P4, P5 간 중심 연결법(centroid linkage method)으로 계산한 수정된 거리행렬(distance matrix) 구하기

 

중심 연결법을 이용한 군집 간 거리는 두 군집 중심의 유클리드 제곱거리를 사용합니다.

 

 

 

한개만 예를 들자면, 군집 (P1, P2)와 개체 P5 간의 중심 연결법에 의한 거리는 위의 [centroid coordinate of clusters - no.1] 의 중심 좌표를 가지고 유클리드 제곱거리로 구하면

d{(P1, P2), P5} = (1.5-5)^2 + (1-4)^2 = 21.25  가 됩니다.

 

[distance matrix - no.2]

 

 

  • P4과 P5의 거리가 '2'로서 가장 가까우므로  
    → (P4, P5)를 새로운 군집으로 묶어줍니다(merge). 이제 군집이 처음 5개에서 3개로 줄었습니다.

 

 

5) 새로운 군집 (P4, P5)의 중심(centroid) 구하기

 

[centroid coordinate of clusters - no.2]

 

 

 

수정된 2차원 부분집합그림은 아래와 같습니다. (P1, P2) 군집에 이어 두번째로 (P4, P5) 군집이 묶였습니다.  노란색 별은 군집의 중심(centroid)를 의미합니다.

 

 

 

 

 

6) 군집 (P1, P2), P3, (P4, P5) 간의 거리를 중심 연결법(centroid linkage method)으로 계산하여 수정한 거리행렬(distance matrix) 구하기

 

유클리드 제곱거리를 사용해서 군집의 중심(centroid) 간의 거리를 계산하였습니다.

 

[distance matrix - no.3]

 

 

  • 개체 P3와 군집 (P4, P5)의 거리가 '6.5'로서 가장 가까우므로 
    → P3과 (P4, P5)를 새로운 군집으로 묶어줍니다(merge). 반복(repeat)을 거듭할 수록 군집이 줄어서 이제 2개가 되었습니다. 

 

 

7) 새로 합쳐진 군집 {P3, (P4, P5)} 의 중심(centroid)를 가중 평균을 사용해서 구하기

 

 

[centroid coordinate of clusters - no.3]

 

 

 

여기까지 진행한 군집화 결과를 반영해 수정한 부분집합그림은 아래와 같습니다.

 

 

 

 

 

8) 군집 (P1, P2)와 {P3, (P4, P5)} 의 중심 간 거리를 중심 연결법(centroid link)으로 계산하여 수정한 거리 행렬(distance matrix) 구하기

 

 

 

  • 마지막으로 두개 남은 군집 (P1, P2)와 {P3, (P4, P5)}를 묶어줍니다(merge).  
    → 드디어 반복(repeat)을 거듭한 끝에 군집이 1개로 줄어들었습니다. 
        → 종료 (End) 

 

마지막 군집이 병합된 이후의 수정된 부분집합그림은 아래와 같습니다.

 

 

 

 

이상의 '중심 연결법(centroid linkage method)'에 의한 응집형 계층적 군집화를 위한 R script는 아래와 같습니다.

 

> # Agglomerative Hierarchical Clustering : Centroid Linkage method
> hc_cent <- hclust(dist(xy)^2, method="centroid")
> hc_cent

Call:
hclust(d = dist(xy)^2, method = "centroid")

Cluster method   : centroid 
Distance         : euclidean 
Number of objects: 5 

 

 

 

 

 

9) 덴드로그램(Dendrogram)으로 응집형 계층적 군집화(by 중심 연결법) 결과 확인해보기

 

아래 덴드로그램의 y축이 바로 군집 간 거리 (평균 연결법으로 구한) 를 나타냅니다.

plot(x, hang = -1) 옵션을 설정하면 아래 그램의 오른쪽 덴드로그램처럼 군집 묶어주는 선이 y=0 부터 시작합니다.

 

> # dendrogram
> my_par = par(no.readonly = TRUE)
> par(oma = c(0, 0, 1, 0))
> par(mfrow = c(1, 2))
> plot(hc_cent)
> plot(hc_cent, hang = -1) # hang = -1 : line from the bottom

 

 

 

 

rev() 함수를 사용하면 군집 모델에 대한 정보를 알 수 있습니다.

 - $method : 연결 방법(linkage method)

 - $height : 군집 간 거리(distance between clusters)

 - $merge : 군집 간 병합 순서 (merge sequence)

 

> # custering information
> rev(hc_cent)
$dist.method
[1] "euclidean"

$call
hclust(d = dist(xy)^2, method = "centroid")

$method
[1] "centroid"

$labels
NULL

$order
[1] 1 2 3 4 5

$height
[1]  1.00000  2.00000  6.50000 11.80556

$merge
     [,1] [,2]
[1,]   -1   -2
[2,]   -4   -5
[3,]   -3    2
[4,]    1    3

 

 

 

 

이전 포스팅의 단일(최단) 연결법, 완전(최장) 연결법, 평균 연결법과 비교를 했을 때 평균 연결법과 유사한 정도의 군집 간 거리(R 결과에서는 Height로 표기)로 계산되었네요.

 

> # comparison of height among linkage methods
> hc_sl <- hclust(dist(xy)^2, method="single")
> hc_cl <- hclust(dist(xy)^2, method="complete")
> hc_avg <- hclust(dist(xy)^2, method="average")
> hc_cent <- hclust(dist(xy)^2, method="centroid")
> 
> # dendrogram
> my_par = par(no.readonly = TRUE)
> par(oma = c(0, 0, 1, 0))
> par(mfrow = c(1, 4))
> plot(hc_sl, main = "Single Linkage")
> plot(hc_cl, main = "Complete Linkage")
> plot(hc_avg, main = "Average Linkage")
> plot(hc_cent, main = "Centroid Linkage")

 

 

 

이상으로 (1) 응집형 계층적 군집화(agglomerative hierarchical clustering) 알고리즘의 프로토타입 기반 (1-4) 중심 연결법(Centroid linkage method) 에 대해서 알아보았습니다.

 

[Reference]

(1) "Introduction to Data Mining", Pang-Ning Tan(Michigan State University), Michael Steinbach(University of Minnesota), Vipin Kumar(University of Minnesota), Addison-Wesley Companion Book

(2) "Clustering Algorithm", Ana Fred, INSTITUTO SUPERIOR TECNICO, Universidade Techica de Lisboa, 2009

(3) "R, SAS, MS-SQL을 활용한 데이터마이닝", 이정진 지음, 자유아카데미, 2011

(4) Wikipedia
    - cluster analysis : https://en.wikipedia.org/wiki/Cluster_analysis

    - hierarchical clustering : https://en.wikipedia.org/wiki/Hierarchical_clustering 

 

 

다음번 포스팅에서는 (1) 응집형 계층적 군집화(agglomerative hierarchical clustering) 알고리즘의 프로토타입 기반(Prototype-based) 군집 간 거리 측정법으로 (1-5) Ward 연결법(Ward linkage method)에 대해서 알아보도록 하겠습니다.

 

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

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 외부 텍스트 파일을 불러오기 할 때에

 

 

Error in scan(file = file, what = what, sep = sep, quote = quote, dec = dec, 

: line 3 did not have 5 elements

 

와 같은

(1) 에러 메시지가 뜨는 이유와

(2) 대처 방안

(3) 유의 사항

 

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

 

 

(1) 에러메시지 이유

Error in scan(file = file, what = what, sep = sep, quote = quote, dec = dec, 

: line 3 did not have 5 elements 

 

에러메시지가 뜨는 이유는 3번째 행(line 3)의 원소의 개수가 결측값(missing value) 때문에 총 5개가 안되기 때문입니다. (row name 1개, 행 4개 모두 포함해서 총 5개 원소)

 

아래에 간단한 예를 들어서 설명하겠습니다.

 

cat() 함수를 이용해서 V1, V2, V3, V4 라는 변수명을 가진 4개의 변수에 대해 First, Second, Third, Fourth라는 rowname을 가진 text 파일을 만들어보았습니다. 이때 의도적으로 3번째 행(3rd row, 3 line)에 원소를 row name 포함해서 4개만 만들어보았습니다. (다른 행은 모두 5개 원소, 3행만 4개 원소) 

MyDocument 폴더에 가보면 test.txt 라는 파일이 생성되어 있음을 확인할 수 있으며, 그 파일을 열어보면 아래 화면 캡쳐한 것 처럼 데이터가 들어가 있으며, 3행은 원소가 총 4개이며, 다른 행 대비 1개가 모자람을 알 수 있습니다.  

 

> ##-----------------------------------------------##
> ## Error in scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings,  
> ## :line 3 did not have 5 elements
> ##-----------------------------------------------##
> 
> # exmaple with missing value in line 3 at 5th element
> cat("V1 V2 V3 V4\nFirst 1 2 3 4\nSecond 5 6 7 8\nThird 9 10 11\nFourth 13 14 15 16", file="test.txt")

 

 

 

 

 

 

 

이렇게 생긴 데이터를 read.table() 함수를 써서 읽어들여보겠습니다.

 

> read.table("test.txt", header = TRUE)
Error in scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  : 
  line 3 did not have 5 elements
In addition: Warning message:
In read.table("test.txt", header = TRUE) :
  'test.txt'에서 readTableHeader에 의하여 발견된 완성되지 않은 마지막 라인입니다

 

 

다른 행은 총 5개의 원소(row name 1개, 변수 4개)가 있는데 반해, 3번째 행은 5개 원소가 아니라는 에러 메시지가 떴습니다.  원소가 일부 모자란다는 뜻입니다.

 

 

 

(2) 대처 방안

첫번째 방법은 파일을 열어서 결측값 위치를 찾아가서 그 값에 제대로 된 값을 채워넣는 것입니다.  파일의 데이터 개수가 몇 개 안되고 육안으로 확인해서 채워넣을 수 있는 경우에는 적용가능 하겠지만, 만약 데이터 개수가 수천, 수만, 수십만 개라면 많이 힘들겠지요? ^^;

 

두번째 방법은, 만약 원소의 개수가 모자라는 이유가 제일 마지막 열에 결측값(missing value)가 있기 때문이라면 fill = TRUE 옵션을 부가하면 NA 표기가 부가되면서 읽어들이기를 간단하게 해결할 수 있습니다.

 

> # missing value => NA : fill = TRUE
> testfile <- read.table("test.txt", header = TRUE, fill = TRUE)
Warning message:
In read.table("test.txt", header = TRUE, fill = TRUE) :
  'test.txt'에서 readTableHeader에 의하여 발견된 완성되지 않은 마지막 라인입니다
> 
> testfile
       V1 V2 V3 V4
First   1  2  3  4
Second  5  6  7  8
Third   9 10 11 NA
Fourth 13 14 15 16

 

 


 

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

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. R Friend R_Friend 2016.06.17 11:26 신고  댓글주소  수정/삭제  댓글쓰기

    DB에서 데이터 내릴때 구분자(delimeter) 설정 잘 해주는게 중요합니다.

군집 간 거리를 측정하는 방법에 따라서 여러가지 알고리즘이 있는데요, 지난번 포스팅에서는 응집형 계층적 군집화(agglomerative hierarchical clustering) 알고리즘 중에서 (1-1) 단일(최단) 연결법 (single linkage method, MIN), (1-2) 완전(최장) 연결법 (complete linkage method, MAX) 에 대해 소개하였습니다.

 

이번 포스팅에서는 응집형 계층적 군집화 알고리즘 중에서 그래프 기반(Graph-based) 방법의 세번째로 (1-3) 평균 연결법 (average linkage method)에 대해서 알아보겠습니다.

 

 

 

 

 

 

평균 연결법은 서로 다른 군집 간의 모든 짝을(pair-wise) 이룬 점들의 평균 거리로 유사성을 측정합니다.

아래의 그래프 기반 군집 간 유사성 측정(graph-based mesaures of cluster proximity) 방법으로서 (1-1) 단일(최단) 연결법, (1-2) 완전(최장) 연결법과 (1-3) 평균 연결법의 수식, 그리고 도식을 보면 서로의 차이점을 이해할 수 있을 것입니다.

 

 

 

 

이제 2차원 공간(2-dimentional space)에 5개의 점을 가지고 간단한 예를 들어서 설명을 해보겠습니다.

 

지난번 포스팅의 단일(최단) 연결법, 완전(최장) 연결법 예시 데이터와 동일하며, 거리 계산 방법도 아래의 (1)번, (2)번까지는 똑같고, (3)번부터는 조금 다릅니다. 위의 수식 처럼 평균 개념이 들어가 있어서 단일 연결법, 완전 연결법 대비 조금 복잡한 감이 있습니다.

 

 

1) 데이터셋 준비, 탐색적 분석

 

응집형 계층적 군집화이므로 처음에 아래의 5개의 점, 5개의 군집에서부터 시작합니다.

 

 

 

R script도 같이 제시하면서 설명하겠습니다.  먼저, 데이터 입력 및 plotting (↑ 위의 산점도 그래프) 입니다.

 

> ##--------------------------------------------
> ## (1) Agglomerative Hierarchical Clustering 
> ##    (1-3) Average Linkage
> ##--------------------------------------------
> 
> x <- c(1, 2, 2, 4, 5)
> y <- c(1, 1, 4, 3, 4)
> 
> xy <- data.frame(cbind(x, y))
> 
> xy
  x y
1 1 1
2 2 1
3 2 4
4 4 3
5 5 4
> 
> # scatter plot of xy
> plot(xy, pch = 19, xlab = c("x coordinate"), ylab = c("y coordinate"), 
+      xlim = c(0, 6), ylim = c(0, 6), 
+      main = "scatter plot of xy")
> 
> # adding student label
> text(xy[,1], xy[,2], labels = abbreviate(rownames(xy)), 
+      cex = 0.8, pos = 1, col = "blue") # pos=1 : at the bottom
> 
> 
> # adding dotted line
> abline(v=c(3), col = "gray", lty = 2) # vertical line
> abline(h=c(3), col = "gray", lty = 2) # horizontal line

 

 

 

2) 유사성 측도로서 거리 행렬(Distance matrix) D 계산하기

 

거리 측도는 분석 목적, 데이터 특성에 맞게 선택해야 하는데요, 이번 예제에서는 '유클리드 제곱거리(squares of Euclidean distance)'를 사용하겠습니다. 계산 및 설명의 편의를 위해서 유클리드 거리를 제곱했습니다.

 

[distance matrix - no.1]

 

 

유클리드 제곱거리를 구하는 R script 입니다. dist(xy, method="euclidean") 에다가 뒤에 "^2"를 붙여서 제곱을 했습니다.(이해, 설명 편의를 위해... 유클리드 거리로 하면 매번 squared root 씌우는 것이 귀찮아서요.) 

 

> # proximity matrix : squares of euclidean distance matrix for 6 points
> dist(xy, method = "euclidean")^2
   1  2  3  4
2  1         
3 10  9      
4 13  8  5   
5 25 18  9  2

 

 

 

  • P1과 P2의 거리가 '1'로서 가장 가까우므로 (즉, 유사하므로) 
    → (P1, P2)를 새로운 군집으로 묶어줍니다(merge). 이제 군집이 처음 5개에서 4개로 줄었습니다.

 

2차원 데이터에 대해서는 아래처럼 부분집합그림(Nested cluster diagram)을 그려볼 수 있습니다.

 

 

(여기까지는 단일 연결법, 완전 연결법과 동일합니다)

 

 

3) 군집 (P1, P2)와 개체 P3, P4, P5 간의 거리를 평균 연결법(average linkage method)으로 수정한 거리행렬(distance matrix) 구하기

 

평균 연결법은 다른 군집 간의 모든 짝을 이룬 점들의 평균 거리로 유사성을 측정하는 방법이므로 아래의 계산 절차를 따릅니다. 

 

 

 

한개만 예를 들자면, 군집 (P1, P2)와 개체 P5 간의 평균 연결법에 의한 거리는 

d{(P1, P2), P5} = {d(P1, P5) + d(P2, P5)}/(2*1) = (25 + 18)/2 = 43/2 = 21.5

가 됩니다.

 

 

단일(최단) 연결법으로 하면 MIN값인 18 이 되며, 완전(최장) 연결법으로 하면 MAX값인 25가 되는 반면에, 평균 연결법으로 하면 단일 연결법과 완전연결법의 평균 지점인 21.5가 되었습니다.  (MIN 값이든 MAX값이든 Average 값이든 간에 이상값, 특이값에 민감하다는 점은 유의해야 겠습니다)

 

 

아래 거리행렬의 d(P3, P4), d(P3, P5), d(P4, P5)는 위의 거리행렬 [distance matrix - no.1]에서 계산한 결과를 그대로 가져다가 썼습니다.

 

[distance matrix - no.2]

 

 

 

  • P4과 P5의 거리가 '2'로서 가장 가까우므로  
    → (P4, P5)를 새로운 군집으로 묶어줍니다(merge). 이제 군집이 처음 5개에서 3개로 줄었습니다.

 

수정된 2차원 부분집합그림은 아래와 같습니다. (P1, P2) 군집에 이어 두번째로 (P4, P5) 군집이 묶였습니다.

 

 

 

 

4) 군집(P1, P2)와 군집(P4, P4), 개체 P3 간의 거리를 평균 연결법(average linkage method)으로 수정한 거리행렬(distance matrix) 구하기

 

군집 간 거리 계산 방법은 동일하게 '평균 거리'를 계산하면 됩니다.

 

[distance matrix - no.3]

 

 

 

  • 개체 P3와 군집 (P4, P5)의 거리가 '9'로서 가장 가까우므로 
    → P3과 (P4, P5)를 새로운 군집으로 묶어줍니다(merge). 반복(repeat)을 거듭할 수록 군집이 줄어서 이제 2개가 되었습니다. 

 (여기까지도 군집 묶이는 순서가 이전의 단일(최단) 연결법, 완전(최장) 연결법과 결과가 같습니다. 예제를 급하게 만들었더니 그만...^^;  서로 좀 다른게 있어야지 실감을 할텐데요... ^^;;;  그래도 군집 간 거리는 단일 연결법과 완전 연결법의 중간 즈음, 즉 MIN과 MAX사이의 평균값으로 바뀌었음을 알 수는 있습니다. ^^;;;;;)

 

 

여기까지 진행한 군집화 결과를 반영해 수정한 부분집합그림은 아래와 같습니다.

 

 

 

 

5) 군집(P1, P2)와 군집{P3, (P4, P5)} 간의 거리를 평균 연결법으로 수정한 거리행렬 구하기

 

군집 간 평균 연결법을 사용한 거리 계산은 아래와 같습니다.  조금 복잡해 보일 수도 있는데요, 단순 사칙연산의 연속입니다.

 

 

 

  • 마지막으로 두개 남은 군집 (P1, P2)와 {P3, (P4, P5)}를 묶어줍니다(merge).  
    → 드디어 반복(repeat)을 거듭한 끝에 군집이 1개로 줄어들었습니다. 
        → 종료 (End) 

 

마지막 군집이 병합된 이후의 수정된 부분집합그림은 아래와 같습니다.

 

 

 

 

이상의 '완전(최장) 연결법'에 의한 응집형 계층적 군집화를 위한 R script는 아래와 같습니다.  역시 단 한줄로 짧습니다.

 

> # Agglomerative Hierarchical Clustering : Average Linkage method
> hc_avg <- hclust(dist(xy)^2, method="average")
> hc_avg

Call:
hclust(d = dist(xy)^2, method = "average")

Cluster method   : average 
Distance         : euclidean 
Number of objects: 5 


 

 

 

 

6) 덴드로그램(Dendrogram)으로 응집형 계층적 군집화(by 평균 연결법) 결과 확인해보기

 

아래 덴드로그램의 y축이 바로 군집 간 거리 (평균 연결법으로 구한) 를 나타냅니다.

plot(x, hang = -1) 옵션을 설정하면 아래 그램의 오른쪽 덴드로그램처럼 군집 묶어주는 선이 y=0 부터 시작합니다.

 

아래 덴드로그램의 Height가 위에서 수기로 계산한 군집 간 거리와 일치하지요?  가령, 가장 마지막에 병합된 군집 (P1, P2)와 군집 {P3, (P4, P5)} 간의 거리가 손으로 계산한 거리가 13.83이었으며, 아래 덴드로 그램도 14에서 조금 못미치고 있습니다.

 

> # dendrogram
> my_par = par(no.readonly = TRUE)
> par(oma = c(0, 0, 1, 0))
> par(mfrow = c(1, 2))
> plot(hc_avg)
> plot(hc_avg, hang = -1) # hang = -1 : line from the bottom
> 

 

 

 

 

rev() 함수를 사용하면 군집 모델에 대한 정보를 알 수 있습니다.

 - $method : 연결 방법(linkage method)

 - $height : 군집 간 거리(distance between clusters)

 - $merge : 군집 간 병합 순서 (merge sequence)

 

 >
> # custering information
> rev(hc_avg)
$dist.method
[1] "euclidean"

$call
hclust(d = dist(xy)^2, method = "average")

$method
[1] "average"

$labels
NULL

$order
[1] 1 2 3 4 5

$height
[1]  1.00000  2.00000  7.00000 13.83333

$merge
     [,1] [,2]
[1,]   -1   -2
[2,]   -4   -5
[3,]   -3    2
[4,]    1    3

 

 

 

이전 포스팅의 단일(최단) 연결법과 완전(최장) 연결법 대비 중간 정도의 군집 간 거리(R 결과에서는 Height로 표기)로 계산되었음을 알 수 있습니다.

 

> # comparison of height among linkage methods
> hc_sl <- hclust(dist(xy)^2, method="single")
> hc_cl <- hclust(dist(xy)^2, method="complete")
> hc_avg <- hclust(dist(xy)^2, method="average")
> 
>   # dendrogram
> my_par = par(no.readonly = TRUE)
> par(oma = c(0, 0, 1, 0))
> par(mfrow = c(1, 3))
> plot(hc_sl, main = "Single Linkage")
> plot(hc_cl, main = "Complete Linkage")
> plot(hc_avg, main = "Average Linkage")

 

 

 

 

이상으로 (1) 응집형 계층적 군집화(agglomerative hierarchical clustering) 알고리즘의 (1-3) 평균 연결법(Average linkage method) 에 대해서 알아보았습니다.

 

[Reference]

(1) "Introduction to Data Mining", Pang-Ning Tan(Michigan State University), Michael Steinbach(University of Minnesota), Vipin Kumar(University of Minnesota), Addison-Wesley Companion Book

(2) "Clustering Algorithm", Ana Fred, INSTITUTO SUPERIOR TECNICO, Universidade Techica de Lisboa, 2009

(3) "R, SAS, MS-SQL을 활용한 데이터마이닝", 이정진 지음, 자유아카데미, 2011

(4) Wikipedia
    - cluster analysis : https://en.wikipedia.org/wiki/Cluster_analysis

    - hierarchical clustering : https://en.wikipedia.org/wiki/Hierarchical_clustering 

 

 

다음번 포스팅에서는 (1) 응집형 계층적 군집화(agglomerative hierarchical clustering) 알고리즘의 프로토타입 기반(Prototype-based) 군집 간 거리 측정법으로 (1-4) 중심 연결법(Centroid linkage method)에 대해서 알아보도록 하겠습니다.

 

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

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 1466138406 2016.06.17 13:40  댓글주소  수정/삭제  댓글쓰기

    좋은 정보 잘보고 갑니다

군집 간 거리를 측정하는 방법에 따라서 여러가지 알고리즘이 있는데요, 지난번 포스팅에서는 응집형 계층적 군집화(agglomerative hierarchical clustering) 알고리즘 중에서 (1-1) 단일(최단) 연결법 (single linkage method, MIN) 에 대해 소개하였습니다.

 

 

이번 포스팅에서는 응집형 계층적 군집화 알고리즘 중에서 그래프 기반(Graph-based) 방법의 두번째로 (1-2) 완전(최장) 연결법 (complete linkage method, MAX)에 대해서 알아보겠습니다.

 

 

 

 

 

단일(최단) 연결법이 군집 간 거리를 잴 때 다른 군집의 점들 중에서 가장 가까운 두 점 간의 거리를 사용하였다면, 완전(최장) 연결법은 반대로 '다른 군집의 점들 중에서 가장 멀리 떨어진 두 점 간의 거리를 가지고 군집 간 거리를 잽니다. 

 

아래의 가운데 그림은 군집 k와 군집 i+j (← 군집 i와 군집 j가 합쳐진 새로운 군집) 간의 거리를 완전(최장) 연결법으로 구할 때의 이미지인데요, 보시면 금방 이해할 수 있을 것입니다.

 

 

 

 

이제 2차원 공간(2-dimentional space)에 5개의 점을 가지고 간단한 예를 들어서 설명을 해보겠습니다.

(지난번 포스팅의 단일(최단) 연결법 예시 데이터와 동일하며, 거리 계산 방법도 아래의 (1)번, (2)번까지는 똑같고, (3)번부터는 매우 유사함. 단일연결법을 정확히 이해했다면 이번 완전연결법은 그야말로 누워서 떡먹기임)

 

 

1) 데이터셋 준비, 탐색적 분석

 

응집형 계층적 군집화이므로 처음에 아래의 5개의 점, 5개의 군집에서부터 시작합니다.

(결측값, 이상값/특이값 여부 check)

 

 

 

R script도 같이 제시하면서 설명하겠습니다.  먼저, 데이터 입력 및 plotting (↑ 위의 산점도 그래프) 입니다.

 

> ##--------------------------------------------
> ## (1) Agglomerative Hierarchical Clustering 
> ##    (1-2) Complete Linkage, MAX
> ##--------------------------------------------
> 
> x <- c(1, 2, 2, 4, 5)
> y <- c(1, 1, 4, 3, 4)
> 
> xy <- data.frame(cbind(x, y))
> 
> xy
  x y
1 1 1
2 2 1
3 2 4
4 4 3
5 5 4
> 
> # scatter plot of xy
> plot(xy, pch = 19, xlab = c("x coordinate"), ylab = c("y coordinate"), 
+      xlim = c(0, 6), ylim = c(0, 6), 
+      main = "scatter plot of xy")
> 
> # adding student label
> text(xy[,1], xy[,2], labels = abbreviate(rownames(xy)), 
+      cex = 0.8, pos = 1, col = "blue") # pos=1 : at the bottom
> 
> 
> # adding dotted line
> abline(v=c(3), col = "gray", lty = 2) # vertical line
> abline(h=c(3), col = "gray", lty = 2) # horizontal line

 

 

 

 

 

2) 유사성 측도로서 거리 행렬(Distance matrix) D 계산하기

 

거리 측도는 분석 목적, 데이터 특성에 맞게 선택해야 하는데요, 이번 예제에서는 '유클리드 제곱거리(squares of Euclidean distance)'를 사용하겠습니다. 계산 및 설명의 편의를 위해서 유클리드 거리를 제곱했습니다.

 

[distance matrix - no.1]

 

 

 

유클리드 제곱거리를 구하는 R script 입니다. dist(xy, method="euclidean") 에다가 뒤에 "^2"를 붙여서 제곱을 했습니다.(이해, 설명 편의를 위해... 유클리드 거리로 하면 매번 squared root 씌우는 것이 귀찮아서요.) 

 

> # proximity matrix : squares of euclidean distance matrix for 6 points
> dist(xy, method = "euclidean")^2
   1  2  3  4
2  1         
3 10  9      
4 13  8  5   
5 25 18  9  2

 

 

 

 

  • P1과 P2의 거리가 '1'로서 가장 가까우므로 (즉, 유사하므로) 
    → (P1, P2)를 새로운 군집으로 묶어줍니다(merge). 이제 군집이 처음 5개에서 4개로 줄었습니다.

 

2차원 데이터에 대해서는 아래처럼 부분집합그림(Nested cluster diagram)을 그려볼 수 있습니다.

 

 

 

 

3) 군집 (P1, P2)와 개체 P3, P4, P5 간의 거리를 완전(최장) 연결법으로 수정한 거리행렬 구하기

 

완전(최장) 연결법은 다른 군집에 속한 가장 멀리 떨어져 있는 두 점의 거리를 군집 간 거리로 측정하는 방법이므로 아래의 계산 절차를 따릅니다. 

 

한개만 예를 들자면, 군집 (P1, P2)와 개체 P5 간의 거리는 d(P1, P5)=25, d(P2, P5)=18 의 두 거리 중에서 최대값(MAX)인 '25'가 됩니다.(↔ 단일(최단) 연결법은 최소값(MIN)을 거리로 채택).  단일(최단) 연결법 대비 군집-군집(개체) 간 거리가 더 길어졌습니다.

 

아래 거리행렬의 d(P3, P4), d(P3, P5), d(P4, P5)는 위의 거리행렬 [distance matrix - no.1]에서 계산한 결과를 그대로 가져다가 썼습니다.

 

[distance matrix - no.2]

 

 

 

  • P4과 P5의 거리가 '2'로서 가장 가까우므로  
    → (P4, P5)를 새로운 군집으로 묶어줍니다(merge). 이제 군집이 처음 5개에서 3개로 줄었습니다.

 

(※ 혹시 완전(최장) 연결법이므로 이 단계에서 최장(가장 큰) 값을 가지고 군집을 묶는 것으로 혼돈하시면 안됩니다.  최장 거리(MAX)는 거리행렬(distance matrix) 구하는 단계에서 사용했구요, 새로운 군집 merge할 때는 서로 가까운 점들 끼리 묶어주어야 합니다. 공교롭게도 아래의 부분집합그림이 이전 포스팅의 단일연결법의 결과와 같습니다. ^^; 거리의 길이는 완전(최장) 연결법이 더 멀리 떨어진 것으로 좀 달라졌구요.)

 

 

 

 

 

 

4) 군집(P4, P5)와 군집(P1, P2), P3 간의 거리를 완전(최장) 연결법으로 수정한 거리행렬 구하기

 

군집 간 거리 계산 방법은 동일하게 '최대값(MAX)'을 찾으면 됩니다.

 

[distance matrix - no.3]

 

 

 

  • 개체 P3와 군집 (P4, P5)의 거리가 '9'로서 가장 가까우므로 
    → P3과 (P4, P5)를 새로운 군집으로 묶어줍니다(merge). 반복(repeat)을 거듭할 수록 군집이 줄어서 이제 2개가 되었습니다. 

 (여기까지도 군집 묶이는 순서가 이전의 단일(최소) 연결법과 결과가 같습니다. 예제를 급하게 만들었더니 그만...^^;  서로 좀 다른게 있어야지 실감을 할텐데요... ^^;;;)

 

 

 

 

 

5) 군집(P1, P2)와 군집{P3, (P4, P5)} 간의 거리를 완전(최장) 연결법으로 수정한 거리행렬 구하기

 

 

 

 

  • 마지막으로 두개 남은 군집 (P1, P2)와 {P3, (P4, P5)}를 묶어줍니다(merge).  
    → 드디어 반복(repeat)을 거듭한 끝에 군집이 1개로 줄어들었습니다. 
        → 종료 (End) 

 

 

 

이상의 '완전(최장) 연결법'에 의한 응집형 계층적 군집화를 위한 R script는 아래와 같습니다.  역시 단 한줄로 짧습니다. 참 편한 세상입니다. ㅎㅎ 

 

> # Agglomerative Hierarchical Clustering : Complete Linkage method (MAX) > hc_cl <- hclust(dist(xy)^2, method="complete") > hc_cl Call: hclust(d = dist(xy)^2, method = "complete") Cluster method : complete Distance : euclidean Number of objects: 5

 

 

 

 

6) 덴드로그램(Dendrogram)으로 응집형 계층적 군집화(by 완전(최장) 연결법) 결과 확인해보기

 

아래 덴드로그램의 y축이 바로 군집 간 거리 (완전(최장) 연결법으로 구한) 를 나타냅니다.

plot(x, hang = -1) 옵션을 설정하면 아래 그램의 오른쪽 덴드로그램처럼 군집 묶어주는 선이 y=0 부터 시작합니다.

 

이전 포스팅의 단일(최단) 연결법 대비 완전(최장) 연결법으로 구한 군집 간 거리(R 결과에서는 Height로 표기)가 더 길게 계산되었음을 알 수 있습니다.

 

> # dendrogram > my_par = par(no.readonly = TRUE) > par(oma = c(0, 0, 1, 0)) > par(mfrow = c(1, 2)) > plot(hc_cl) > plot(hc_cl, hang = -1) # hang = -1 : line from the bottom >

 

 

 

 

 

 

rev() 함수를 사용하면 군집 모델에 대한 정보를 알 수 있습니다.

 - $method : 연결 방법(linkage method)

 - $height : 군집 간 거리(distance between clusters)

 - $merge : 군집 간 병합 순서 (merge sequence)

 

> # custering information > rev(hc_cl) $dist.method [1] "euclidean" $call hclust(d = dist(xy)^2, method = "complete") $method [1] "complete" $labels NULL $order [1] 1 2 3 4 5 $height [1] 1 2 9 25 $merge [,1] [,2] [1,] -1 -2 [2,] -4 -5 [3,] -3 2 [4,] 1 3

 

 

 

이상으로 (1) 응집형 계층적 군집화(agglomerative hierarchical clustering) 알고리즘의 (1-2) 완전(최장) 연결법(complete linkage method, MAX) 에 대해서 알아보았습니다.

 

[Reference]

(1) "Introduction to Data Mining", Pang-Ning Tan(Michigan State University), Michael Steinbach(University of Minnesota), Vipin Kumar(University of Minnesota), Addison-Wesley Companion Book

(2) "Clustering Algorithm", Ana Fred, INSTITUTO SUPERIOR TECNICO, Universidade Techica de Lisboa, 2009

(3) "R, SAS, MS-SQL을 활용한 데이터마이닝", 이정진 지음, 자유아카데미, 2011

(4) Wikipedia
    - cluster analysis : https://en.wikipedia.org/wiki/Cluster_analysis

    - hierarchical clustering : https://en.wikipedia.org/wiki/Hierarchical_clustering 

 

 

다음번 포스팅에서는 (1) 응집형 계층적 군집화(agglomerative hierarchical clustering) 알고리즘의 그래프 기반(Graph-based) 군집 간 거리 측정법으로 (1-3) 평균 연결법(Average linkage method)에 대해서 알아보도록 하겠습니다.

 

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

 

Posted by R Friend R_Friend

댓글을 달아 주세요

지난번 포스팅에서는 군집분석의 유형과 알고리즘 종류, 그리고 유사성 측도로서의 거리에 대해서 알아보았습니다.

 

이번 포스팅에서는 세부적인 분석 알고리즘의 첫번째로서, 각 데이터에서 시작해서 유사한(즉, 가까운) 데이터끼리 순차적으로 군집을 묶어나가는 응집형 계층적 군집화 (Agglomerative Hierarchical Clustering, "Bottom-up") 알고리즘에 대해서 알아보겠습니다. 

 

(↔ 참고로, 분리형(Divisive) 계층적 군집화 알고리즘은 하나의 군집에서 시작해 Top-down으로 군집을 나누어가서 각 데이터가 하나의 군집이 될 때까지 분화 반복)

 

 

 

 

 

 

응집형 계층적 군집화 알고리즘은 아래와 같습니다. 거리 행렬을 가지고 군집 간 유사성을 측정해서 모든 데이터가 하나의 군집으로 응집/병합될 때까지 반복적으로 군집화를 수행합니다.

 

 

 

 

 

위의 알고리즘에 보면 유사성 행렬(proximity matrix), 거리 행렬(distance matrix)가 나오는데요, 지난번 포스팅에서 소개했었던 다양한 거리 측도 중에서 데이터 특성, 분석 목적에 맞는 거리 측도를 선택해서 가능한 모든 쌍의 데이터 간 거리를 계산하면 됩니다. (유사성 측도로 거리를 사용하면 => "거리가 짧을 수록 유사하다"고 해석)

 

 

 

 

응집형 계층적 군집화 방법에는 "군집 간 거리를 측정하는 방법"에 따라서 여러가지가 있습니다. 이번 포스팅에서는 그래프 기반(Graph-based)의 연결법으로 '단일(최단) 연결법 (Single linkage method)'를 소개하겠습니다. 그래프 기반 연결법은 각 데이터를 Node로 하고, Nodes 간 연결(connectivity)한 선을 Link or Arc로 하며, 이들 Link or Arc 의 길이를 가지고 거리를 측정합니다.

 

 

 

"단일 연결법(Single linkage method)""최단(MIN) 연결법"이라고도 하며, 다른 군집에 속한 가장 가까운 두 점 사이의 거리를 군집 간의 거리로 측정하는 방법입니다. 

 

아래 그림은 군집 k와 군집 i+j (← 군집 i와 군집 j가 합쳐진 새로운 군집) 간의 거리를 단일(최단) 연결법으로 구할 때의 이미지입니다.

 

 

 

 

이제 2차원 공간(2-dimentional space)에 5개의 점을 가지고 간단한 예를 들어서 설명을 해보겠습니다.

 

1) 데이터셋 준비, 탐색적 분석

 

아래의 5개의 점 하나 하나가 각 군집(cluster)이 되겠습니다.  응집형 계층적 군집화이므로 처음에 아래의 5개의 점, 5개의 군집에서부터 시작합니다.

 

데이터셋에 결측값 없고, 산점도를 그려보니 이상값/특이값(outlier)도 없어 보이는군요.

 

 

 

R script도 같이 제시하면서 설명하겠습니다.  먼저, 데이터 입력 및 plotting (↑ 위의 산점도 그래프) 입니다.

 

> ##--------------------------------------------
> ## (1) Agglomerative Hierarchical Clustering 
> ##    (1-1) Single Linkage, MIN
> ##--------------------------------------------
> 
> x <- c(1, 2, 2, 4, 5)
> y <- c(1, 1, 4, 3, 4)
> 
> xy <- data.frame(cbind(x, y))
> 
> xy
  x y
1 1 1
2 2 1
3 2 4
4 4 3
5 5 4
> 
> # scatter plot of xy
> plot(xy, pch = 19, xlab = c("x coordinate"), ylab = c("y coordinate"), 
+      xlim = c(0, 6), ylim = c(0, 6), 
+      main = "scatter plot of xy")
> 
> # adding student label
> text(xy[,1], xy[,2], labels = abbreviate(rownames(xy)), 
+      cex = 0.8, pos = 1, col = "blue") # pos=1 : at the bottom
> 
> 
> # adding dotted line
> abline(v=c(3), col = "gray", lty = 2) # vertical line
> abline(h=c(3), col = "gray", lty = 2) # horizontal line

 

 

 

 

2) 유사성 측도로서 거리 행렬(Distance matrix) D 계산하기

 

거리 측도는 분석 목적, 데이터 특성에 맞게 선택해야 하는데요, 이번 예제에서는 '유클리드 제곱거리(squares of Euclidean distance)'를 사용하겠습니다. 계산 및 설명의 편의를 위해서 유클리드 거리를 제곱했습니다.

 

[distance matrix - no.1]

 

유클리드 제곱거리를 구하는 R script 입니다. dist(xy, method="euclidean") 에다가 뒤에 "^2"를 붙여서 제곱을 했습니다.(이해, 설명 편의를 위해...) 

 

> # proximity matrix : squares of euclidean distance matrix for 6 points
> dist(xy, method = "euclidean")^2
   1  2  3  4
2  1         
3 10  9      
4 13  8  5   
5 25 18  9  2

 

 

  • P1과 P2의 거리가 '1'로서 가장 가깝군요. 
    → (P1, P2)를 새로운 군집으로 묶어줍니다(merge). 이제 군집이 처음 5개에서 4개로 줄었습니다.

 

2차원 데이터에 대해서는 아래처럼 부분집합그림(Nested cluster diagram)을 그려볼 수 있습니다.

 

 

 

3) 군집 (P1, P2)와 개체 P3, P4, P5 간의 거리를 단일(최단) 연결법으로 수정한 거리행렬 구하기

 

단일(최단) 연결법은 다른 군집에 속한 가장 가까운 두 점의 거리를 군집 간 거리로 측정하는 방법이므로 아래의 계산 절차를 따릅니다. 

 

한개만 예를 들자면, 군집 (P1, P2)와 개체 P5 간의 거리는 d(P1, P5)=25, d(P2, P5)=18 의 두 거리 중에서 최소값(MIN)인 '18'이 됩니다.  아래 거리행렬의 d(P3, P4), d(P3, P5), d(P4, P5)는 위의 거리행렬 [distance matrix - no.1]에서 계산한 결과를 그대로 가져다가 썼습니다.

 

[distance matrix - no.2]

 

  • P4과 P5의 거리가 '2'로서 가장 가깝군요. 
    → (P4, P5)를 새로운 군집으로 묶어줍니다(merge). 이제 군집이 처음 5개에서 3개로 줄었습니다.

 

 

 

 

 

4) 군집(P4, P5)와 군집(P1, P2), P3 간의 거리를 단일(최단) 연결법으로 수정한 거리행렬 구하기

 

군집 간 거리 계산 방법은 동일하게 '최소값'을 찾으면 됩니다.

 

[distance matrix - no.3]

 

 

  • 개체 P3와 군집 (P4, P5)의 거리가 '5'로서 가장 가깝군요. 
    → P3과 (P4, P5)를 새로운 군집으로 묶어줍니다(merge). 반복(repeat)을 거듭할 수록 군집이 줄어서 이제 2개가 되었습니다. 

 

 

 

 

5) 군집(P1, P2)와 군집 {P3, (P4, P5)} 간의 거리를 단일(최단) 연결법으로 수정한 거리행렬 구하기

 

 

 

  • 마지막으로 두개 남은 군집 (P1, P2)와 {P3, (P4, P5)}를 묶어줍니다(merge).  
    → 드디어 반복(repeat)을 거듭한 끝에 군집이 1개로 줄어들었습니다. 
        → 종료 (End) 

 

 

 

이상의 '단일(최단) 연결법'에 의한 응집형 계층적 군집화를 위한 R script는 아래와 같습니다.  너무 짧다고 놀라지 마세요. ㅋㅋ 

위에서 알고리즘 설명한다고 개난리를 쳤는데요, R로는 단 1줄이면 끝납니다.

(설명자료 쓰고 그리는데는 토요일 반나절 투자, R script 짜는데는 5분... )

 

> # Agglomerative Hierarchical Clustering : Single Linkage method (MIN)
> hc_sl <- hclust(dist(xy)^2, method="single")
> hc_sl

Call:
hclust(d = dist(xy)^2, method = "single")

Cluster method   : single 
Distance         : euclidean 
Number of objects: 5

 

 

 

 

6) 덴드로그램(Dendrogram)으로 응집형 계층적 군집화(by 단일(최단) 연결법) 결과 확인해보기

덴드로그램(Dendrogram)은 (a) '무슨 군집과 무슨 군집이 서로 묶였는지 (서로 유사한지, 근접한지)', (b) '어떤 순서로 차례대로 묶여갔는지', (c) '군집 간 거리는 얼마나 되는지'를 알 수 있는 매우 유용한 그래프입니다.

 

아래 덴드로그램의 y축이 바로 군집 간 거리 (단일(최소) 연결법으로 구한) 를 나타냅니다.

plot(x, hang = -1) 옵션을 설정하면 아래 그램의 오른쪽 덴드로그램처럼 군집 묶어주는 선이 y=0 부터 시작합니다.

 

> # dendrogram
> my_par = par(no.readonly = TRUE)
> par(oma = c(0, 0, 1, 0))
> par(mfrow = c(1, 2))
> plot(hc_sl)
> plot(hc_sl, hang = -1) # hang = -1 : line from the bottom

 

 

 

rev() 함수를 사용하면 군집 모델에 대한 정보를 알 수 있습니다.

 - $method : 연결 방법(linkage method)

 - $height : 군집 간 거리(distance between clusters)

 - $merge : 군집 간 병합 순서 (merge sequence)

 

> # custering information > rev(hc_sl) $dist.method [1] "euclidean" $call hclust(d = dist(xy)^2, method = "single") $method [1] "single" $labels NULL $order [1] 1 2 3 4 5 $height [1] 1 2 5 8 $merge [,1] [,2] [1,] -1 -2 [2,] -4 -5 [3,] -3 2 [4,] 1 3

 

 

위의 rev() 함수 결과의 객체를 indexing 해서 군집 간 특정 거리를 기준(threshold)으로 해서 기준거리 이상인 군집의 개수를 알아보겠습니다.

 

예를 들어서 군집 간 거리가 '6' 이상인 군집은 위의 덴드로그램에서 보면 군집(P1, P2)와 군집 {P3, (P4, P5)}의 2개가 있습니다. (아래 R script의 세번째 예시)

 

>
> # number of clusters above distance's threshold
>   # exmaple 1) height threshold = 1
> height_threshold <- 1
> distance_over_height <- rev(hc_sl)$height >= height_threshold
>
> number_of_cluster <- sum(distance_over_height)+1
> number_of_cluster
[1] 5
>
>
>   # exmaple 2) height threshold = 3
> height_threshold <- 3
> distance_over_height <- rev(hc_sl)$height >= height_threshold
>
> number_of_cluster <- sum(distance_over_height)+1
> number_of_cluster
[1] 3
>
>   # exmaple 3) height threshold = 6
> height_threshold <- 6
> distance_over_height <- rev(hc_sl)$height >= height_threshold
>
> number_of_cluster <- sum(distance_over_height)+1
> number_of_cluster
[1] 2 

 

이상으로 (1) 응집형 계층적 군집화 : (1-1) 단일(최단) 연결법(Single link, MIN) 소개를 마치도록 하겠습니다. 다 쓰고 보니 스크롤 압박이 상당히 심한...블로그에는 부적절한 글이 되어버렸습니다. ㅜ_ㅠ

 

[Reference]

(1) "Introduction to Data Mining", Pang-Ning Tan(Michigan State University), Michael Steinbach(University of Minnesota), Vipin Kumar(University of Minnesota), Addison-Wesley Companion Book

(2) "Clustering Algorithm", Ana Fred, INSTITUTO SUPERIOR TECNICO, Universidade Techica de Lisboa, 2009

(3) "R, SAS, MS-SQL을 활용한 데이터마이닝", 이정진 지음, 자유아카데미, 2011

(4) Wikipedia
    - cluster analysis : https://en.wikipedia.org/wiki/Cluster_analysis

    - hierarchical clustering : https://en.wikipedia.org/wiki/Hierarchical_clustering 

 

다음번 포스팅에서는 응집형 계층적 군집화의 두번째로 (1-2) '완전(최장) 연결법'(Complete linkage method, MAX)에 대해서 알아보도록 하겠습니다.

 

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

 

 


Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 리암 2016.09.18 14:33  댓글주소  수정/삭제  댓글쓰기

    정말고생하셨습니다. 친절만 설명 감사드려요.

  2. 궁금 2018.11.29 23:08  댓글주소  수정/삭제  댓글쓰기

    1차원 데이터에 대한 군집 시각화 방법은 없을까요??

  3. 셈모 2019.04.16 15:56  댓글주소  수정/삭제  댓글쓰기

    지금 군집분석 공부하고있는데, 이전글들 읽어오면서 정말 친절한 설명에 너무 감사하고있습니다. ㅜㅜ 감사합니다!

  4. 어제보다오늘 2019.10.03 21:14  댓글주소  수정/삭제  댓글쓰기

    와진짜 대단! 너무 깔끔히 정리되어있어요 감사합니다!

지난번 포스팅에서는 (구간식 또는 비율식 데이터 속성의 다변수일 경우의) 유사성, 비유사성 측도로서 다양한 거리의 정의에 대해서 알아보았습니다.

 

이번 포스팅에서는 하나의 예를 가지고 R을 사용하여 각 거리 종류별 거리를 계산, 비교해보도록 하겠습니다.

 

예로 사용할 데이터는 5명의 학생의 '선형대수(linear algebra)'와 '기계학습(machine learning)' 기말고사 시험성적입니다.  이 중에서 1번째와 2번째 학생(아래 산점도의 빨간점)의 선형대수와 기계학습 시험점수의 거리를 (1) 맨하탄 거리(manhattan distance), (2) 유클리드 거리(euclidean distance), (3) 표준화 거리(standadized distance), (4) 마할라노비스 거리(mahalanobis distance)로 계산해보도록 하겠습니다.

 

> ##----------------------------------
> ## Dis-similarity measure - Distance
> ##----------------------------------
> 
> # 'linear algebra' and 'machine learning' score per student
> st_1 <- c(70, 65)
> st_2 <- c(90, 95)
> st_3 <- c(65, 60)
> st_4 <- c(85, 90)
> st_5 <- c(60, 75)
> 
> st_all <- rbind(st_1, st_2, st_3, st_4, st_5)
> st_all
     [,1] [,2]
st_1   70   65
st_2   90   95
st_3   65   60
st_4   85   90
st_5   60   75
> 
> # scatter plot
> plot(st_all, xlab = "linear algebra", ylab = "machine learning", 
+      xlim = c(50, 100), ylim = c(50, 100), 
+      main = "scatter plot of final scores")
> 
> # adding student label
> text(st_all[,1], st_all[,2], labels = abbreviate(rownames(st_all)), 
+      cex = 0.8, pos = 1, col = "blue")
> 
> # marking student 1, student 2 with red point
> points(st_all[1,1], st_all[1,2], col = "red", pch = 19)
> points(st_all[2,1], st_all[2,2], col = "red", pch = 19)

 

 

 

 

 

1) 맨하탄 거리 (Manhattan distance)

 

dist(dataset, method = "manhattan") 함수를 사용하여 맨하탄 거리를 계산할 수 있습니다.

 

아니면 아래의 두번째 방법처럼 두 변수의 관측치별 차이 절대값을 더해주면 됩니다 ( <- 달랑 2개 관측치 거리를 계산하는 거니깐 이 방식도 사용할만 하지만요, 만약 관측치 학생 1,000 명의 거리를 구하는 것이라면요? ^^;  두 방식의 결과값 포맷이 조금 다르다는 점도 눈여겨 봐주세요.)

 

 

> ##-- Manhattan distance between 1st and 2nd student
> ## (1) dist(dataset, method = "manhattan") function
> dist_mht <- dist(st_all[1:2, ], method = "manhattan")
> dist_mht
     st_1
st_2   50
> 
> ##-- Manhattan distance between 1st and 2nd student
> ## (2) calculation
> dist_mht_2 <- abs(st_all[1,1] - st_all[2,1]) + abs(st_all[1,2] - st_all[2,2])
> dist_mht_2
st_1 
  50 

 

 

 

 

2) 유클리드 거리 (Euclidean distance)

 

dist(dataset, method = "euclidean") 함수를 사용하여 유클리드 거리를 계산할 수 있습니다.

 

 

> ##-- Euclidean distance between 1st and 2nd student
> dist_euclid <- dist(st_all[1:2, ], method = "euclidean")
> dist_euclid
         st_1
st_2 36.05551

 

 

 

 

 

3) 표준화 거리, 통계적 거리 (Standadized distance, Statistical distance)

 

표준화 거리는 2가지 방법을 사용해서 계산해보겠습니다.

  (1) 데이터를 표준화한 후에 -> 유클리드 거리 계산

  (2) 

 

'2.501805' 로서 두 가지 계산 방법의 결과가 똑같습니다.

 

> ##-- Standadized distance > # (way 1) standadization -> euclidean distance > st_all [,1] [,2] st_1 70 65 st_2 90 95 st_3 65 60 st_4 85 90 st_5 60 75 > mean(st_all[,1]); sd(st_all[,1]) [1] 74 [1] 12.94218 > mean(st_all[,2]); sd(st_all[,2]) [1] 77 [1] 15.24795 > > # standadization of st_1's score > z_st_1 <- (st_all[,1] - mean(st_all[,1]))/sd(st_all[,1]) > z_st_1 st_1 st_2 st_3 st_4 st_5 -0.3090670 1.2362679 -0.6954007 0.8499342 -1.0817344 > > # standadization of st_2's score > z_st_2 <- (st_all[,2] - mean(st_all[,2]))/sd(st_all[,2]) > z_st_2 st_1 st_2 st_3 st_4 st_5 -0.7869910 1.1804865 -1.1149039 0.8525736 -0.1311652 > > z_st_all <- cbind(z_st_1, z_st_2) > z_st_all z_st_1 z_st_2 st_1 -0.3090670 -0.7869910 st_2 1.2362679 1.1804865 st_3 -0.6954007 -1.1149039 st_4 0.8499342 0.8525736 st_5 -1.0817344 -0.1311652 > > # euclidean distance between 1st and 2nd student's standadized score > dist_z_st_all <- dist(z_st_all[1:2, ], method = "euclidean") > dist_z_st_all st_1 st_2 2.501805 > > # ======================================================================= > # (way 2) [(X-Y)'D^-1(X-Y)]^1/2

> # covariance : cov()

> cov_st_all <- cov(st_all)

> cov_st_all

      [,1]  [,2]
[1,] 167.5 165.0
[2,] 165.0 232.5

> Diag <- rbind(c(cov_st_all[1,1], 0), + c(0, cov_st_all[2,2])) > Diag [,1] [,2] [1,] 167.5 0.0 [2,] 0.0 232.5 > > dist_stand <- sqrt(t(st_all[1,] - st_all[2,])%*%solve(Diag)%*% + (st_all[1,] - st_all[2,])) > dist_stand # exactly the same with the above result [,1] [1,] 2.501805

 

 

 

 

 

4) 마할라노비스 거리 (Mahalanobis distance) 

마할라노비스 거리는 아래 식을 사용해서 계산해보겠습니다.

 

 

 

> ##-- Mahalanobis distance
> # covariance : cov()
> cov_st_all <- cov(st_all)
> cov_st_all
      [,1]  [,2]
[1,] 167.5 165.0
[2,] 165.0 232.5
> dist_mahal <- sqrt(t(st_all[1,] - st_all[2,])%*%solve(cov_st_all)%*%
+                      (st_all[1,] - st_all[2,]))
> dist_mahal
         [,1]
[1,] 1.975854

 

 

 

위에서 계산한 4가지 방법별 거리 계산 결과를 종합하면 아래와 같습니다.

맨하탄 거리가 가장 길고 > 유클리드 거리 > 표준화 거리 > 마할라노비스 거리 순으로 나왔네요.

 

> ## overall
> dist_all <- cbind(dist_mht[1], dist_euclid[1], dist_stand, dist_mahal)
> dist_all
     [,1]     [,2]     [,3]     [,4]
[1,]   50 36.05551 2.501805 1.975854
> 
> barplot(dist_all, 
+         col = "blue", 
+         names.arg =  c("manhattan", "euclidean", "standadized", "mahalanobis"), 
+         main = "Distance between st_1 and st_2's score")

 

 

 

이상으로 군집분석 들어가기 전에 기초 체력을 다지는 의미에서 비유사성 측도로서의 거리(distance)에 대해서 알아보았습니다.

 

다음번 포스팅에서는 본격적으로 군집분석 세부 내용으로 들어가 보겠습니다. 첫 테잎은 '계층적 군집(hierarchical clustering) 모형'이 끊는 것으로 해보겠습니다.

 

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

 

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 닥터케빈 2017.11.09 11:38  댓글주소  수정/삭제  댓글쓰기

    안녕하세요? 먼저 좋은 내용의 포스팅 잘 봤습니다.

    오늘 '마할라노비스 거리'에 대해서 궁금한 게 있어서 이 포스팅에 오게 되었습니다.
    예시로 올려주신 내용은 첫 번째 학생과 두 번째 학생 간의 유사도를 다양한 거리 측도로 비교해주신 것으로 보입니다.

    저는 주어진 3차원 행렬 X에 대해서 '마할라노비스 거리'를 일괄적으로 계산하고 싶어서 다른 사이트를 찾아보니
    mahalonobis() 함수가 있고, 아래와 같이 실행을 하면 거리 벡터가 출력되는 것 같습니다.
    mahalonobis(x=X, center=colmeans(X), cov=cov(X))

    이와 같은 경우, 각 row 간 거리가 아닌 각 row별 거리를 계산해 주는 것이 맞겠죠?
    그러니까 이 결과값이 큰 것을 이상치로 가정하고 제외하면 무리가 없는지 궁금합니다. ^^

    • R Friend R_Friend 2017.11.12 19:35 신고  댓글주소  수정/삭제

      안녕하세요 닥터케빈님,
      답변 늦어서 정말 죄송합니다.
      퇴근 후에 답변 단다는게 그만 깜빡했습니다. ^^;;;

      거리는 row 간 거리, 즉 각 관측치 간 거이입니다.

      저는 mahalanobis() 함수가 있는줄 댓글로 달아주셔서 처음 알았는데요, 유용하게 잘 쓰겠습니다. mahalanobis() 함수 적용하면 각 관측치간 거리가 matrix 형태로 나오지요?

      이 마하라노비스 거리 행렬을 가지고 군집분석을 수행해서 outlier 성격의 군집을 detect 해보는 시도도 의미있을 것 같습니다.

      단, 댓글에 질문 남겨주신 것처럼 거리가 row 별 거리는 아닙니다.

  2. 어렵다어려워 2019.04.16 14:09  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 r 프로그래밍 검색하다가 보게됐는데요
    유클리드 거리 구할때 1번학생과 2번학생 유클리드 거리 구하는법은 알겠는데
    1번학생과 3번학생의 유클리드 거리는 어떻게 구하는지 모르겠네요 ..
    r 돌려보니까 첨자의 혀용범위를 넘었다고 하는데
    시간되시면 답변부탁드립니다...!

이번 포스팅에서는 벡터(vector)와 행렬(matrix)를 자유자재로 넘나들며 데이터 변환하는 방법을 소개하겠습니다. R에는 벡터와 행렬을 아주 쉽게 넘나들게 해주는 함수가 있답니다.

 

(1) 벡터를 행렬로 변환하기

    (1-1) 벡터를 열(column) 기준으로 행렬로 변환하기 : matrix(vector, byrow = FALSE, ncol = n)

    (1-2) 벡터를 행(row) 기준으로 행렬로 변환하기 : matrix(vector, byrow = TRUE, ncol = n)

 

(2) 행렬을 벡터로 변환하기 (vectorization)

    (2-1) 행렬을 열(column) 기준으로 벡터로 변환하기 : as.vector(matrix)

    (2-2) 행렬을 행(row) 기준으로 벡터로 변환하기 : as.vector( t(matrix) )

 

 


 

 

(1) 벡터를 행렬로 변환하기 (converting vector into matrix)

 

 

 

 

    (1-1) 벡터를 열(column) 기준으로 행렬로 변환하기

 

> ##-------------------------------------------------------
> ## vector into matrix, matrix into vector (vectorization)
> ##-------------------------------------------------------
> 
> # input vector
> vec <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
> vec
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
> 
> # (1-1) vector into matrix by column
> matrix_bycol <- matrix(vec, byrow = F, ncol = 3)
> matrix_bycol
     [,1] [,2] [,3]
[1,]    1    6   11
[2,]    2    7   12
[3,]    3    8   13
[4,]    4    9   14
[5,]    5   10   15

 

 

 

 

    (1-2) 벡터를 행(row) 기준으로 행렬로 변환하기

 

> # (1-2) vector into matrix by row
> matrix_byrow <- matrix(vec, byrow = T, ncol = 3)
> matrix_byrow
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9
[4,]   10   11   12
[5,]   13   14   15

 

 

 

 


 

 

 

(2) 행렬을 벡터로 변환하기 (vectorization, converting matrix into vector)

 

 

 

 

    (2-1) 행렬을 열(column) 기준으로 벡터로 변환하기

 

> # (2-1) matrix into vector (vectorization) by column : as.vector()
> matrix_bycol
     [,1] [,2] [,3]
[1,]    1    6   11
[2,]    2    7   12
[3,]    3    8   13
[4,]    4    9   14
[5,]    5   10   15
> vectorization_bycol <- as.vector(matrix_bycol)
> vectorization_bycol
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

 

 

 

    (2-2) 행렬을 행(row) 기준으로 벡터로 변환하기

 

> # (2-2) matrix into vector (vectorization) by row : as.vector(t())
> matrix_bycol
     [,1] [,2] [,3]
[1,]    1    6   11
[2,]    2    7   12
[3,]    3    8   13
[4,]    4    9   14
[5,]    5   10   15
> vectorization_byrow <- as.vector(t(matrix_bycol))
> vectorization_byrow
 [1]  1  6 11  2  7 12  3  8 13  4  9 14  5 10 15 

 

 

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

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

지난번 포스팅에서는 군집분석(Cluster Analysis)의 개념과 유형에 대해서 알아보았습니다.

 

군집분석은 데이터를 유사한 혹은 동질의 군집(cluster)으로 묶어주는 것이라고 했었는데요, 그렇다면 "유사하다"(Similarity) 혹은 "유사하지 않다"(Dis-similarity)를 어떻게 측정할 수 있을지에 대한 방법이 이번 포스팅의 주제입니다.

 

이번에 살펴볼 거리 측도는 데이터와 데이터간 (비)유사성을 보는 군집분석뿐만이 아니라 변수와 변수간 관계를 보는 다변량 통계 분석에서도 기본기가 되는 중요한 내용이므로 유심히 보시기 바랍니다.  

 

데이터 간의 비유사성(Dis-similarity)거리(Distance)를 가지고 주로 측정하며, 유사성(Similarity)은 비유사성과 반비례의 관계에 있다고 보면 됩니다.  거리 말고 상관계수(Correlation coefficient)를 쓰기도 하는데요(→ collaborative filtering에서 상관계수 사용함), 이번 포스팅에서는 거리만 설명하도록 하겠습니다.

 

거리는 데이터의 속성, 구조에 따라서 적합한 것을 사용해야 하는데요, 이번 포스팅에서는 다중 변수의 구간식(Interval data type) 또는 비율식(Ratio data type) 데이터 속성에 대한 비유사성 측도로서 '거리'(Distance as a dis-similarity mesaure)척도로서,

 

  (1) 맨하탄 거리 (Manhattan distance)

  (2) 유클리드 거리 (Euclid distance)

  (3) 표준화 거리 (Standardized distance)

  (4) 마할라노비스 거리 (Mahalanobis distance)

 

에 대해서 소개하겠습니다.  이밖에 확장 자카르드 계수(Extended Jaccard Coefficient), 상관계수(Correlation Coefficient), 브레그만 거리(Bregman Divergence)는 생략합니다.

 

[ 표기 관련 (denotation) ]

- 두 변수값 x와 y비유사성 척도로서 거리(distance) 는 d(x, y) 또는 간단히 d 로 표기함

 

 

[ 거리 척도 (Distance measures) ]

 

 

 

(1) 맨하탄 거리 (Manhattan distance)

 

 

맨하탄 거리는 뉴욕의 택시가 출발지에서 도착지로 갈 때 빌딩을 피해 동, 서, 남, 북의 격자 모양의 도로를 직선으로 갈 때의 거리(즉, 가로 블록 + 세로 블록 절대합)를 본따서 이름을 붙힌 거리입니다. ("Manhattan distance" is a rectilinear distance, named after the number of blocks north, south, east, or west a taxicab must travel on to reach its destination on the grid of streets in parts of New York City. - from Wikipedia -).  물리적인 길거리를 생각하면 쉽습니다. 자동차가 아무리 마음이 급하다고 길과 길 사이에 있는 빌딩(방해물, obstacle)을 뚫고 대각선으로 지나갈 수는 없는 노릇이며, 좋으나 싫으나 아래 그림처럼 빌딩을 피해 '도로'가 놓여진 한계 안에서 '최단 route'를 찾는 수밖에 없습니다. (<= 바로 이런 상황의 데이터를 분석해야할 때 맨하탄 거리를 사용하면 됩니다)

 

다른 말로 "City block distance", "Chessboard distance", "Chebyshev distance" 라고도 합니다. 서양 체스의 Rook move (상/하/좌/우, 동/서/남/북 방향으로만 이동 가능)를 생각하면 됩니다.

 

두 점의 값(starting point -> destination)에 대한 맨하탄 거리(Manhattan Distance)를 그림과 수식으로 나타내면 아래와 같습니다.  그래프 이론(Graph theory)에서는 두 지점의 거리를 '최단 경로(shortest route)'로 정의하는데요, 아래 그림에서 보면 각 도러의 블록별 단위거리가 동일하다고 하면 최단경로가 route 1, route 2, route 3, .... 많습니다 (좌에서 우로 8번 & 하에서 상으로 7번 경로 조합인 모든 route).

 

 

맨하탄 거리(Manhattan distance)는 민코우스키 거리(Minkowski distance)에서 r=1 인 거리이기도 합니다.  r=2 (2-norm distance) 인 경우는 유클리드 거리 (Euclidean distance) 입니다.

 

 

For a point (x1, x2, ...,xm) and a point (y1, y2, ...,ym), Minkowski distance is...

 

 

 

 

 

(2) 유클리드 거리 (Euclidean distance)

 

유클리드 거리는 두 점을 잇는 가장 짧은 직선 거리입니다.  아래 그림을 예로 들면, 헬리콥터를 타고 x 지점에서 y지점으로 날아간다고 했을 때 x와 y 지점 사이에 아무리 많은 빌딩(방해물)이 있더라도 상관없이 최단 직선 코스로 날아갈 수 있다고 연상하면 쉽게 이해할 수 있을 것입니다. 창공을 나는 새의 관점(from a bird's eye view)으로 본 거리입니다.  (↔ 위의 택시를 타고 가는 맨하탄 거리와의 차이를 비교해보세요)

 

서양 체스로 치면 상, 하, 좌, 우, 대각선 어느 방향으로나 이동이 자유로운 King move 나 Queen move 를 생각하면 되겠네요. (↔ 맨하탄 거리는 Rook move)

 

m차원 유클리드 공간 (Euclidean space ) 에서 두 점 (x1, x2), (y1, y2)의 거리는 피타고라스 정리(Pythagorean theorem)에 의해서 아래 그림에서 제시한 공식으로 구할 수 있습니다. 

 

 

민코우스키 거리(Minkowski distance)에서 r=2 (2-norm distance) 인 거리가 바로 유클리드 거리 (Euclidean distance) 입니다

 

 

 

 

만약 세 점 (x1, x2, x3), (y1, y2, y3) (in three-space) 간의 거리는 어떻게 구하면 될까요?  이 또한 피타고라스 정리에 의해서 아래처럼 유클리드 거리 공식으로 구하면 됩니다.

 

 

아주 많은 분석에서 유클리드 거리를 사용하며, 그렇다보니 대부분 '거리'하면 '유클리드 거리'를 가장 먼저 떠올리게 되곤 합니다.  그만큼 친숙하고도 아주 유용한 거리 개념입니다.

 

그런데 유클리드 거리에도 한가지 고려해야 할 점이 있습니다.  만약 두 변수의 분산, scale이 서로 다르다면 어떻게 될까요?  ☞ 표준화 거리에 대해서 얘기할 시간이군요.

 

 

 

 

(3) 표준화 거리 (Standardized distance)

 

 표준화 거리는 각 변수를 해당변수의 표준편차(standard deviation)로 척도 변환한 후에 유클리드 거리를 계산한 거리입니다.  표준화를 하게 되면 척도(scale)의 차이, 분산의 차이로 인한 왜곡을 피할 수 있습니다.  

표준화 거리는 다른 말로 통계적 거리 (Statistical distance) 라고도 합니다.

 

 

 

표준화 거리를 이용하여 한 점에서 거리가 같은 점들의 집합을 구하면 표본평균을 중심으로 좌표축에 장축(major axis)과 단축(minor axis)이 반듯하게 놓인 타원(Ellipse) 또는 타원체를 이루게 됩니다. 변수가 2개인 경우 두 개체간의 표준화 거리를 구하면 타원의 중심은 각 변수의 평균값이 위치한 곳이 되며, 아래 그림과 같은 형태로 그려져 각 변수의 평균점을 중심으로 하는 타원이 됩니다. ("R 다변량 통계분석", 김재희)

 

 

 

 

 

(4) 마할라노비스 거리 (Mahalanobis distance)

 

마할라노비스 거리는 변수의 표준편차와 더불어 변수 간 상관성(correlation)까지 고려한 거리측도입니다. (참고로, 마할라노비스(Prasanta Chandra Mahalanobis, 29 June 1893 – 28 June 1972) 는 인도의 과학자 겸 응용통계학자로서 '마할라노비스 거리'를 제안한 분입니다.)

 

 

 

한 점에서 마할라노비스 거리가 같은 점들의 집합을 구하면 표본평균을 중심으로 축이 회전된 타원체(rotated ellipse)를 이루게 됩니다. 변수간의 상관성이 있을 때의 거리 측도로서 방향성을 고려하여 아래의 그림과 같이 회전된 축을 생각할 수 있습니다. ("R 다변량 통계분석", 김재희)

(↔ 바로 앞의 그림 '표준화 거리를 이용해 구한 한점에서 거리가 같은 점들의 집합 : 타원'과 비교해보세요)

 

 

 

맨하탄 거리와 유클리드 거리는 그래도 제법 접해봤을 기회가 있었을 것 같은데요, 표준화 거리와 마할라노비스 거리는 낯설기도 하고 수식도 선형대수 표기법이 나오고 해서 이해하기가 좀 어려울것 같습니다. 

 

수식을 자세히 보시면 SVD(Singular Value Decomposition), 특이값 분해(축 rotation -> scale 변환 -> 축 rotation)와 비슷함을 알 수 있습니다.

 

다변량통계분석 하는 분이라면 Hotelling's T-squared 통계량 수식과 매우 유사함을 알 수 있을거예요. 마할라노비스 거리를 1/n 로 나누어주면 Hotelling's T-squared 통계량이 됩니다. 재미있죠? ^^

 

다음번 포스팅에서는 간단한 예를 들어서 유클리드 거리, 표준화 거리, 마할라노비스 거리를 설명하고, R script 도 소개하도록 하겠습니다.

 

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

 

[Reference]

(1) Distance from Wikipedia (https://en.wikipedia.org/wiki/Distance)

(2) "R 다변량 통계분석", 김재희 지음, 교우사

(3) Mahalanobis distance from Wikipedia (https://en.wikipedia.org/wiki/Mahalanobis_distance)

 

참고로, 명명식(Norminal) 데이터에 대한 비유사성 척도로는 단순일치계수(Simple Matching Coefficient, SMC), 자카드 계수(Jaccard Coefficient, JC), 문서 비유사도 측정에 코사인 거리(cosine distance), 문자열 편집 거리(edit distance, Levenshtein metric)를 사용합니다.  순서식(Ordinal) 데이터에 대한 비유사성 척도로는 순위상관계수(Rank Correlation Coefficient)를 사용합니다.

 

Posted by R Friend R_Friend

댓글을 달아 주세요