23  대쉬보드

저자가 디지털 글쓰기를 하면서 가장 충격을 받은 사례 중 하나가 flexdashboard 패키지를 접하면서다. 고정관념으로 대쉬보드(Dashboard) 제작은 웹사이트를 제작하는 것이라 소프트웨어 개발자가 아니면 불가능하다고 생각했다. 즉, HTML, CSS, JavaScript 등을 활용하여 웹사이트를 제작하는데 그래프와 표, Value 박스 등 데이터 과학 구성요소가 필요하기 때문에 통계전공 데이터 과학자와 공동으로 작업을 해야 한다고 생각했다. 하지만, flexdashboard패키지로 R 마크다운을 활용해 쉽게 대시보드를 만들 수 있어 HTML, CSS, JavaScript에 대한 지식이 없어도 대시보드를 제작할 수 있다는 점이다.

지금은 잊혀져가고 있지만, 몇년전 코로나19가 전세계를 휩쓸고 있을 때 국내 및 해외 코로나 진행현황을 대쉬보드로 필자가 제작한 사례가 있다. 이 대쉬보드는 flexdashboard 패키지를 활용하여 제작되었으며, 데이터셋은 coronavirus 패키지를 통해 자동으로 최신 데이터가 갱신되기 때문에 대쉬보드 개발 노력을 상당부분 절감시킬 수 있으며 flexdashboard에서 제공하는 다양한 레이아웃 기능을 통해 대시보드 설계를 수월히 진행할 수 있으며 DT, highcharter, leaflet, plotly 등 인터랙티브 패키지로 미려한 시각화 산출물도 제작이 가능하다. 마지막으로 gh-pages를 통해 무료지만 안정성이 가장 뛰어난 웹사이트에 배포도 했다.

23.1 대쉬보드 제작과정

대쉬보드 제작을 순차적으로 진행할 수도 있지만 경우에 따라서는 병렬도 동시작업도 가능하다. 가장 일반적인 순차적 대쉬보드 제작과정은 다음과 같다. 먼저, 전세계와 한국의 코로나19 데이터를 수집하고 수집된 데이터를 분석 가능한 형태로 데이터를 전처리하여 대쉬보드 각 구성요소에 맞게 데이터셋을 맞춘다. 대시보드 UI/UX 디자인을 계획하면서 적절한 시각화 도구(highcharter, leaflet, plotly)에 대한 기술적인 검토도 함께 진행한다. (Kulkarni, 2019)

데이터와 UI/UX 디자인과 기술검토가 마무리된 다음, 대시보드 구성요소를 본격적으로 highcharter, ValueBox, DT 테이블, Plotly 그래프 등으로 개발한다. 대쉬보드 외양을 정의하는 대시보드 브랜딩과 디자인 작업은 CSS/SCSS 스타일링 작업을 통해 진행된다. 대시보드 테스트 단계는 모든 구성요소가 올바르게 작동하는지 테스트하고, 완성된 대시보드를 깃헙 페이지 웹사이트에 배포한다. 이후 자동화 및 업데이트 운영 및 유지보수 단계에서 데이터가 실시간으로 갱신되도록 자동화시키고 신규 개발 코드와 UI/UX 코드도 CI/CD를 통해 자동화한다.

대쉬보드 제작 흐름

23.2 코로나19 대쉬보드

코로나19 대시보드 제작은 여러 단계로 구성된다. 먼저, 코로나19 데이터를 사용할 수 있는데 데이터는 coronavirus 데이터 팩키지로 제작되어 데이터 수집 과정이 간소화되고 이미 잘 구성되어 있어 데이터 수집에 따른 공수를 크게 줄일 수 있다. 데이터를 수집한 후, flexdashboard를 주요 엔진으로 사용하여 대시보드를 제작한다. flexdashboard는 R 마크다운을 기반으로 하며, 다양한 시각화 도구와 통합이 쉽다.

23.2.1 데이터셋

코로나 19 데이터셋은 존스 홉킨스 대학(Johns Hopkins University)과 세계보건기구(WHO)에서 제공되는 두가지 형태로 제공되고 있지만 존스 홉킨스 대학 데이터셋이 WHO 데이터셋 보다 더 많은 호응을 받아 coronavirus 데이터 패키지를 사용한다.

```{r install-dataset}
library(tidyverse)
# install.packages("coronavirus") 
# devtools::install_github("RamiKrispin/coronavirus")
library(coronavirus)

data("coronavirus")

coronavirus <- coronavirus %>% 
  janitor::clean_names() %>% 
  rename(country = country_region,
         province = province_state)
```

23.2.2 대쉬보드 디자인

데이터 과학 요소가 들어간 대쉬보드 제작을 위한 UI 설계안을 작성한다. 디자인 작업을 완료한 후, 와이어프레임(wireframe)을 중심으로 데이터 과학 요소가 포함된 대시보드 UI 설계작업을 진행한다.

코로나19 대쉬보드 UI 설계

아이콘과 디자인 요소를 위해 Font Awesome, Ionicons, Bootstrap, Bootstrap과 같은 라이브러리를 활용하여 다양한 아이콘과 UI 컴포넌트를 통해 대시보드 시각적 표현을 풍부하게 한다.

작성한 wireframe을 기반으로 flexdashboard 문법에 맞춰 대시보드 구성요소를 적절히 배치하고 R 마크다운 코드로 대시보드를 구현한다. 전체적으로 ValueBox를 대시보드 상단에 배치하고, 아래 두 그래프 영역에 필요한 그래프를 삽입하고 전세계와 한국을 탭(tabset)으로 구분하여 표현하여 대쉬보드 복잡성을 단순화한다.

flexdashboard 배치도(layout)

---
layout: page
title: "데이터 과학을 위한 저작도구: Computational Documents"
subtitle: "대쉬보드(Dashboard)"
author:
    name: "[Tidyverse Korea](https://www.facebook.com/groups/tidyverse/)"
date: "2023-10-03"
output:
  html_document: 
    toc: yes
    toc_float: true
    highlight: tango
    code_folding: show
    number_section: true
    self_contained: true
editor_options: 
  chunk_output_type: console
---

전세계 {data-icon="fa-globe"}
===================================

Row {data-width=150} 
--------------------------------------------

### 확진자수

::: {.cell}

```{.r .cell-code}
infected <- 100
valueBox(infected, icon = "fa-procedures")
```
:::

### 사망자수

::: {.cell}

```{.r .cell-code}
death <- 20
valueBox(death, icon = "fa-skull")
```
:::

### 회복자수

::: {.cell}

```{.r .cell-code}
recovered <- 30
valueBox(recovered, 
         icon = "fa-walking",
         color = ifelse(recovered > 10, "warning", "primary"))
```
:::


Column {data-height=350}
-------------------------------------

### Chart 1



### Chart 2


한국 {data-icon="fa-map"}
===================================

-- 이하 동일

23.2.3 시각화 구성요소

막대그래프: 코로나19

coronavirus 데이터 팩키지에서 수집한 코로나 관련 데이터를 “경주하는 막대그래프” 형태로 시각화하기 위해, 먼저 gapminder의 경주 막대그래프 사례를 참조한다. 이 사례를 통해 어떻게 데이터를 준비하고 그래프를 구성할지 아이디어를 얻는다. (Reynolds, 2019)

gapminder: 경주하는 막대그래프

데이터 준비가 완료되면, 정적 막대그래프를 먼저 생성하고 애니메이션 기능을 추가하여 동적인 경주 막대그래프를 완성한다. 애니메이션을 작성한 후에는 .gif 파일 형식으로 저장하고 대시보드에 불러와 그래프 영역에 채워넣는다.

```{r}
library(gganimate) 
options(gganimate.nframes = 100)

## 데이터 
corona_ranked_by_date <- coronavirus %>% 
  filter(type == 'confirmed') %>% 
  group_by(country, date) %>% 
  summarise(confirmed = sum(cases)) %>% 
  ungroup() %>% 
  group_by(country) %>% 
  arrange(date) %>% 
  mutate(cum_confirmed = cumsum(confirmed)) %>% # 국가별 누적환자수
  ungroup() %>% 
  group_by(date) %>% 
  arrange(date, -cum_confirmed) %>% # 일별 상위 국가 선정
  mutate(rank = 1:n()) %>% 
  filter(rank <=7) %>% 
  ungroup()

## 테마
library(extrafont)
loadfonts()

corona_theme <- theme_classic(base_family = "NanumGothic") +
  theme(legend.position = "none") +
  theme(axis.text.y = element_blank()) +
  theme(axis.ticks.y = element_blank()) +
  theme(axis.line.y = element_blank()) +
  theme(legend.background = element_rect(fill = "gainsboro")) +
  theme(plot.background = element_rect(fill = "gainsboro")) +
  theme(panel.background = element_rect(fill = "gainsboro"))

corona_plot <- corona_ranked_by_date %>% 
  ggplot() +
    aes(xmin = 0 ,  
        xmax = cum_confirmed) +  
    aes(ymin = rank - .45,  
        ymax = rank + .45,  
        y = rank) +
    facet_wrap(~date) +
    geom_rect(alpha = .7) +
    aes(fill = country) +
    scale_fill_viridis_d(option = "magma",
                       direction = -1) +
    scale_x_continuous(
      limits = c(-15000, 80000)) +
    geom_text(col = "gray13",  
          hjust = "right",  
          aes(label = country),  
          x = -50) +
    labs(fill = NULL) +  
    labs(x = '감염자수') +  
    labs(y = "") +  
    scale_y_reverse() +
    corona_theme

corona_gif <- corona_plot +
  facet_null() +
  aes(group = country) +
  geom_text(x = 55000 , y = -7,  
          family = "NanumGothic",  
          aes(label = as.character(date) ),  
          size = 10, col = "grey18")  +
  gganimate::transition_time(date)

corona_gif # anim_save("images/corona.gif", corona_gif, duration=0.1)

knitr::include_graphics('images/corona.gif') 
```

23.2.4 공간정보 시각화

leaflet 라이브러리를 활용하여 공간정보 시각화 작업을 수행한다. 먼저, coronavirus 데이터 팩키지에서 수집한 코로나 관련 데이터 중 위경도 정보를 추출하여 leaflet에 입력하여 지도 위에 표시한다. leaflet의 다양한 기능을 활용하여 coronavirus 데이터에 담긴 공간정보를 인터랙티브 시각화를 구현한 후 leaflet 지도를 대시보드 일부로 통합한다.

```{r}
library(leaflet)
library(janitor)

coronavirus %>% 
  mutate(geo_name = glue::glue("{country} {province}")) %>% 
  select(-country, -province) %>% 
  group_by(geo_name, lat, long, type) %>%
    summarise(cases = sum(cases, na.rm = TRUE)) %>% 
    spread(type, cases, fill=0) %>% 
    leaflet() %>%
    addTiles() %>% 
    addProviderTiles(providers$OpenStreetMap) %>% 
    addMarkers(lng=~long, lat=~lat, clusterOptions = markerClusterOptions(),
                     popup = ~ as.character(paste0("<strong> 코로나19 현황: ", geo_name, "</strong><br>",
    "-----------------------------------------------------------<br>",
                                                   "&middot; 감염: ", scales::comma(confirmed), "<br>",
                                                   "&middot; 사망: ", scales::comma(death), "<br>",
                                                   "&middot; 회복: ", scales::comma(recovered), "<br>")))
```

23.3 한국 대쉬보드

23.3.1 데이터셋

한국 코로나19관련 데이터를 MBC 장슬기 기자의 도움으로 이성규, “미디어고토사”에서 찾을 수 있었다. 데이터를 google sheet에 정리해 두어서 googlesheets4의 도움으로 수월하게 입수할 수 있다.

문제는 일자별로 데이터 정리가 체계적으로 되어 있지 않고, 코로나 관련 대쉬보드를 구성할 때 한계가 있다.

```{r}
## 데이터셋 --------------------
library(googlesheets4)
library(tidyverse)
library(lubridate)

cv_kor <- read_sheet('https://docs.google.com/spreadsheets/d/1fODH5PZJw9jxwV2GRe85BRQgc3mxdyyIpQ0I6MDJKXc/edit#gid=0')

cv_kor_df <- cv_kor %>% 
  select(날짜, 확진자=`누적 확진자수`, 검사자 = `누적 검사자수`, 사망자, 회복자=`누적 격리해제`) %>% 
  arrange(날짜) %>% 
  mutate(일자 = as.Date(날짜)) %>% 
  group_by(일자) %>% 
  summarise(검사자 = last(검사자),
               확진자 = last(확진자),
               사망자 = last(사망자),
               회복자 = last(회복자)
            )  %>% 
  padr::pad(.)
```

23.3.2 대쉬보드 구성

전세계 코로나 사례와 다르게 검사자수가 포함되어 있어 이를 valueBox에 포함시킨다. 지역정보가 없기 때문에 이를 DT 표로 대신하는데 몇가지 인터랙티브 기능을 추가해서 기본 정렬에 추가하여 칼럼별 검색 및 다양한 파일 형태로 다운로드 가능하도록 한다. 일자별로 구성된 시계열 데이터 특성을 적극 활용하여 선그래프를 인터랙티브하게 구성한다.

한국 대쉬보드 구성

23.3.3 시각화 구성요소

시각화 구성요소로 valueBox, DT, plotly를 선택했기 때문에 이를 순차적으로 모듈화시켜 개발한 후에 대쉬보드에 적용시킨다.

23.3.4 valueBox()

검사자, 확진자, 사망자, 회복자를 valueBox로 만들어 현황을 파악하기 편하게 구성한다. webshot을 사용하면 굳이 화면을 캡쳐하지 않아도 R마크다운 코드에서 직접 가능하게 된다.

valueBox

23.3.5 표와 그래프

인터랙티브 표를 사용하게 되면 일단 기본기능으로 들어가 있는 정렬외에 searchHighlight를 사용해서 검색 기능을 넣고, dom = 'Bfrtip'을 통해 다양한 형태로 데이터를 내려받을 수 있도록 한다.

```{r}
cv_kor_df  <-  
  read_rds("data/cv_kor_df.rds")

cv_kor_df %>%
  arrange(desc(일자)) %>% 
  DT::datatable(filter = 'top',  
          options = list(   searchHighlight = TRUE, pageLength = 15,
                            dom = 'Bfrtip',
                            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')),
          extensions = 'Buttons') %>% 
  DT::formatRound(c("검사자", "확진자", "사망자", "회복자"), digits=0)
```

plotly 라이브러리를 사용하면 정적 그래프를 쉽게 인터랙티브 그래프로 변환할 수 있다. 정적 그래프의 데이터와 설정을 plotly 형식으로 변환시키면 기본적인 인터랙티브 기능이 적용된다. 선 그래프나 막대 그래프에서는 데이터 포인트에 마우스를 가져가면 해당 값이 표시되고, 그래프를 드래그하여 끌어 확대하거나 축소할 수도 있다.

```{r kor-df-viz}
korea_theme <- theme_classic(base_family = "NanumGothic") +
  theme(legend.position = "none") +
  theme(legend.background = element_rect(fill = "gainsboro")) +
  theme(plot.background = element_rect(fill = "gainsboro")) +
  theme(panel.background = element_rect(fill = "gainsboro"))

cv_kor_plot <- cv_kor_df %>% 
  gather(유형, 사람수, -일자) %>% 
  mutate(유형 = factor(유형, levels=c("검사자", "확진자", "회복자", "사망자"))) %>% 
  ggplot(aes(x=일자, y=사람수, color=유형)) +
    geom_line() +  
    geom_point() +
    facet_wrap(~유형, scale="fixed") +
    scale_y_continuous(labels = scales::comma_format(scale = 1)) +
    korea_theme +
    labs(x="", y="") +
    scale_color_viridis_d(option = "magma",
                     direction = -1) 

plotly::ggplotly(cv_kor_plot)
```

23.4 배포 대쉬보드

대시보드 제작이 완료되면 인터넷에 공개하기 위해 gh-pages를 사용해 웹사이트에 배포한다. gh-pagesGitHub에서 제공하는 무료 웹 호스팅 서비스로, R 마크다운 파일을 HTML 형식으로 변환해 웹사이트를 만들 수 있다. gh-pages를 사용하면 웹사이트를 만들기 위한 별도의 서버를 구축할 필요가 없다.

배포 과정은 먼저 GitHub 저장소를 생성하고 대시보드 모든 파일을 업로드한 다음 GitHub 설정에서 gh-pages를 활성화하고 대시보드가 포함된 브랜치를 선택한다. 설정이 완료되면 GitHub은 자동으로 웹사이트를 생성하고 해당 URL을 제공한다.

코로나19 대시보드는 인터넷에 자유롭게 접근 가능하며, 누구나 코로나19 현황을 쉽게 파악하고 이해하도록 도움을 준다. 대쉬보드는 코로나19 대쉬보드 웹사이트에서 확인할 수 있다.

코로나19 전세계 현황

 

코로나19 한국 현황

flexdashboard 코로나19 대쉬보드

 

Kulkarni, S. (2019). Building an HR Dashboard in R using Flexdashboard. Towards Data Science. https://towardsdatascience.com/building-an-hr-dashboard-in-r-using-flexdashboard-76d14ed3f32
Reynolds, G. (2019, 4월). Racing Barchart with gganimate - a quick flipbook | made with Xaringan. R-Ladies. https://evamaerey.github.io/little_flipbooks_library/racing_bars/racing_barcharts.html#1