이번 포스팅에서는 방향을 가진 네트워크 상에서 유량, 에너지, 물자 등의 '네트워크(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