본문 바로가기
빅데이터

[R/시각화] Sankey Diagram

by 수퍼스타 2019. 12. 23.

생키 다이어그램은 흐름을 보여주기 적합한 차트 중 하나이다.

R을 이용해 생키 다이어그램을 그릴 수 있는 몇 가지 방법을 소개한다.

 

 

(자료: 구글 이미지 검색)

 


"Sankey Diagram"

생키 다이어그램(Sankey Diagram)은 흐름(Flow) 다이어그램의 한 종류로서

그 화살표의 너비로 흐름의 양을 비율적으로 보여준다. 

주로 어떤 프로세스에서 에너지, 연료, 비용의 움직임을 시각화하는 데에 사용된다.

예를 들면, 지역이나 국가 간의 에너지, 연료의 거래를 표현하기에 적합하다. 

또한, 시스템 내에서 주된 이동이나 흐름을 시각적으로 강조하며

이는 어떤 항목의 기여도가 가장 높은지 찾는 데에 효과적이다.

(자료: 위키백과)


1. Plotly 패키지

 

## 학습

 

Plotly 패키지를 이용하면 R 외에도 파이썬, 자바를 이용해 생키 다이어그램을 그릴 수 있다.

Plotly 홈페이지에서 Sankey Diagram을 검색하고 예시를 통해 기본적인 구조와 결과물을 확인한다.

 

library(plotly)

p <- plot_ly(
    type = "sankey",
    orientation = "h",

    node = list(
      label = c("A1", "A2", "B1", "B2", "C1", "C2"),
      color = c("blue", "blue", "blue", "blue", "blue", "blue"),
      pad = 15,
      thickness = 20,
      line = list(
        color = "black",
        width = 0.5
      )
    ),

    link = list(
      source = c(0,1,0,2,3,3),
      target = c(2,3,3,4,4,5),
      value =  c(8,4,2,8,4,2)
    )
  ) %>% 
  layout(
    title = "Basic Sankey Diagram",
    font = list(
      size = 10
    )
)

p

 

(자료: https://plot.ly)

 

예시로 올라와 있는 코드를 보니 A1부터 C2까지 라벨을 설정한 뒤,

라벨의 위치(0부터 시작)를 이용해 flow의 값을 입력하면 자동으로 적합한 차트를 만들어 보여주는 것 같다.

 

node 에서 라벨의 이름, 라벨 블록의 색상, 테두리 등을 설정하고,

link 는 각 라벨간 관계를 설정한다.

 

link 아래 source 는 출발 지점(라벨), target 은 목표 지점(라벨), value 는 양을 의미한다.

즉, 위의 예시는 0번 라벨인 A1에서 출발해 2번 라벨인 B1으로 흐르는 양이 8이라는 것을 나타낸다.

 

## 적용

 

기본은 이해했으니 적당한 자료를 받아서 연습해본다.

아래 예제는 통계청에서 받은 '성 연령 및 세대구성별 인구 현황(2018)' 자료를 바탕으로 작성했다.

 

최근 1인 가구가 급증하고 있는데, 1인 가구의 연령별로 성별 비중은 어떤 차이가 있을까?

 

### Plotly 적용
### 자료: 통계청,「인구총조사」
### 2018년 성별 및 연령별 1인가구 현황

library(plotly)
library(dplyr)

count <- plot_ly(type = "sankey",
                 orientation = "h",
                 node = list(
                     label = c("남자", "여자", "미성년자", "20대",
                               "30대", "40대", "50대", "60대", "70대", "80세이상"), 
                     color = c("blue", "red", "green", "green",
                               "green", "green", "green", "green", "green", "green"),
                     pad = 5,
                     thickness = 30,
                     line = list(color = "black", width = 0.5)
                     ),
                 link = list(
                     line = list(color="black", width = 0.2),
                     source = c(0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1),
                     target = c(2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9),
                     value =  c(27623, 542781, 637042, 545819, 543074, 366564, 172025, 71392,
                                30531, 477163, 355695, 318402, 431370, 503340, 498872, 326901)
                     )
                 ) %>% 
    layout(
        title = "성별 및 연령별 1인가구 현황",
        font = list(size = 10, color = "Black")
        )

count

 

(자료: 통계청)

 

오른쪽에 연령대별로 남자와 여자의 비율을 대략적으로 확인할 수 있다.

대충 보니 20대와 50대는 남녀의 비중이 비슷하고 30대와 40대는 남자의 비중이 높다.

그리고 60대, 70대, 80세이상은 여자의 비중이 높은 것이 보인다.

 

얼핏 생각해보면 20대는 주로 대학생이나 사회초년생이니 남녀 비중이 비슷할 것이고,

60대 이상은 남자의 평균 수명이 짧기 때문에 나타나는 현상일 것이다.

 

생키 다이어그램은 위의 예제처럼 단순하게 2단으로 연결하는 것보다는 (귀찮았다...)

맨 위 그림처럼 좀 더 복잡하고 엉켜있는 흐름도를 한 눈에 확인하는 용도로 적합하다.

 

한 번 만들어보니 생키 다이어그램은 R로 쉽게 만들 수 있는 반면에,

세부적인 라벨의 순서라던가 위치 등을 꾸미는 것은 어렵지 않나 싶다.

 

조금 더 꾸미고 싶다면 아래 링크를 참고하자.

 

Plotly 패키지 레퍼런스

 

Figure Reference

The extensive reference of plotly chart attributes for plotly's R library.

plot.ly


2. networkD3 패키지

## 학습

 

# Library
library(networkD3)
library(dplyr)
 
# A connection data frame is a list of flows with intensity for each flow
links <- data.frame(
  source=c("group_A","group_A", "group_B", "group_C", "group_C", "group_E"), 
  target=c("group_C","group_D", "group_E", "group_F", "group_G", "group_H"), 
  value=c(2, 3, 2, 3, 1, 3)
  )
 
# From these flows we need to create a node data frame: it lists every entities involved in the flow
nodes <- data.frame(
  name=c(as.character(links$source), 
  as.character(links$target)) %>% unique()
)
 
# With networkD3, connection must be provided using id, not using real name like in the links dataframe.. So we need to reformat it.
links$IDsource <- match(links$source, nodes$name)-1 
links$IDtarget <- match(links$target, nodes$name)-1
 
# Make the Network
p <- sankeyNetwork(Links = links, Nodes = nodes,
              Source = "IDsource", Target = "IDtarget",
              Value = "value", NodeID = "name", 
              sinksRight=FALSE)
p

 

 

(자료: https://www.r-graph-gallery.com)

 

networkD3 패키지도 plotly 패키지와 큰 차이는 없다.

link 와 node 를 설정하고 출발 지점과 도착 지점의 좌표를 설정해 흐름을 표현한다.

 

## 적용

## networkD3 적용
## 통계청,「인구총조사」
## 2018년 성별 및 연령별 1인가구 현황

library(networkD3)
library(dplyr)

links <- data.frame(
    source=c("남자", "남자", "남자", "남자", "남자", "남자", "남자", "남자", 
             "여자", "여자", "여자", "여자", "여자", "여자", "여자", "여자"), 
    target=c("미성년자", "20대", "30대", "40대", "50대", "60대", "70대", "80세이상", 
             "미성년자", "20대", "30대", "40대", "50대", "60대", "70대", "80세이상"), 
    value=c(27623, 542781, 637042, 545819, 543074, 366564, 172025, 71392,
            30531, 477163, 355695, 318402, 431370, 503340, 498872, 326901)
)

nodes <- data.frame(
    name=c(as.character(links$source), 
           as.character(links$target)) %>% unique()
)

links$sex <- match(links$source, nodes$name)-1 
links$age <- match(links$target, nodes$name)-1

count <- sankeyNetwork(Links = links, Nodes = nodes,
                       Source = "sex", Target = "age",
                       Value = "value", NodeID = "name", 
                       sinksRight=FALSE)
count

 

(자료: 통계청)

 

위 예제와 같은 자료를 networkD3 패키지를 이용해 그려보았다.

일단 색상을 따로 지정해주지 않았는데 자동으로 알록달록 나타난다는 점은 좋았다.

 

연령별 순서는 plotly 패키지와 똑같은 순서로 나타났다.

뭔가 이유가 있어서 저렇게 배치되는 것 같은데...

배치 순서를 바꾸는 방법을 찾아보던가 직접 수정해서 이미지 파일로 추출해 사용해야겠다.

 

plotly 패키지와 차이점은 라벨의 위치가 수직으로만 변경된다는 것이다.

위치를 잡기에는 더 좋지만 조금 더 다양한 모양으로 만들기는 어려울 것 같다.

 


 

이상으로 R을 이용해 Sankey Diagram 생키 다이어그램을 그리는 두 가지 패키지를 알아봤다.

Tableau를 이용해서 그리는 방법도 있는데 그건 훨~씬 복잡하더라... -0-;

그것도 다음 기회에 작성해봐야겠다.

 

끗.

댓글