Shiny modules 小品 〜 値によって色が変わるvalueBox 〜


`shinydashboard` パッケージの `valueBox()` 関数はダッシュボード上に数値を直接表示する際に利用できるウィジェットです。背景に色を付けたり、アイコンを並べたりして見た目を調整することができます。
`renderValueBox()` 関数と `valueBoxOutput()` 関数とを使用すると、サーバー側で動的にvalueBoxを作成することもできます。

本記事で紹介する例では、表示する値が0以下なら赤色、0より大きければ緑色というように、値に応じて動的に見た目が変化するvalueBoxを作成しました。さらにShiny modulesの仕組みを使ってそれを再利用可能にしました。

## デモ

デモアプリを [こちら](https://terashim.shinyapps.io/shiny-example-conditionalValueBox-module/) で公開しています

### スクリーンショット

![スクリーンショット1](https://qiita-image-store.s3.amazonaws.com/0/78813/ba922cae-301e-acb1-06a0-5f91d08896ab.png)

![スクリーンショット2](https://qiita-image-store.s3.amazonaws.com/0/78813/e853a440-f677-1ad3-4722-fc7ff1759a1b.png)

サイドバー内のスライダーを操作するとvalueBoxの値に反映されるようになっています。
値が0以下か1以上かによってvalueBoxの色やアイコンなどが変化します。

### ソースコード

デモアプリのソースコードは `app.R` ファイルただ１つで、以下のような内容になっています（[GitHub](https://github.com/terashim/shiny-example-conditionalValueBox-module)でも同じものを公開しています）:

#### app.R

```r
library(shiny)
library(shinydashboard)

# Shiny modulesを使って conditionalValueBox を定義 ----------
conditionalValueBoxUI <- function(id) {
  ns <- NS(id)
  
  tagList(
    valueBoxOutput(ns("valueBox"))
  )
}

conditionalValueBox <- function(input, output, session, value) {
  output$valueBox <- renderValueBox({
    valueBox(
      subtitle = ifelse(value() > 0, "いいね", "わるいね"),
      value = value(),
      color = ifelse(value() > 0, "green", "red"),
      icon = icon(ifelse(value() > 0, "thumbs-up", "thumbs-down"))
    )
  })
}

# UI側 ----------
ui <- dashboardPage(
  dashboardHeader(title = "Shiny modues 小品"),
  dashboardSidebar(
    sliderInput(inputId = "value1", label = "値1", value = -2, min = -5, max = 5),
    sliderInput(inputId = "value2", label = "値2", value = 1, min = -5, max = 5)
  ),
  dashboardBody(
    h2("値によって色が変わる valueBox"),
    p("値が1以上なら緑色、0以下なら赤色になる"),
    
    fluidRow(column(
      12,
      h3("値1について"),
      conditionalValueBoxUI("vb1")
    )),
    
    fluidRow(column(
      12,
      h3("値2について"),
      conditionalValueBoxUI("vb2")
    ))
  )
)

# サーバー側 ----------
server <- function(input, output) {
  callModule(conditionalValueBox, "vb1", reactive(input$value1))
  callModule(conditionalValueBox, "vb2", reactive(input$value2))
}

# アプリの起動 ----------
shinyApp(ui = ui, server = server)

```

### ポイント

#### モジュールの定義

Shiny modulesの規約にしたがってモジュールを作成するため、`conditionalValueBoxUI` と `conditionalValueBox` という２つの関数を定義しています:

```r
conditionalValueBoxUI <- function(id) {
  ns <- NS(id)
  
  tagList(
    valueBoxOutput(ns("valueBox"))
  )
}

conditionalValueBox <- function(input, output, session, value) {
  output$valueBox <- renderValueBox({
    valueBox(
      subtitle = ifelse(value() > 0, "いいね", "わるいね"),
      value = value(),
      color = ifelse(value() > 0, "green", "red"),
      icon = icon(ifelse(value() > 0, "thumbs-up", "thumbs-down"))
    )
  })
}
```

サーバー側の処理を定義する関数 `conditionalValueBox(input, output, session, value)` で、４番目の引数 `value` はモジュールの呼び出し元から値を受け取るために使用します。
この値に応じて `renderValueBox` の結果が変化するようにしています。

ここで注意点として、この `value` は **reactiveな表現式** であるという前提になっています。単に `value` ではなく後ろに括弧を付けて `value()` という使い方をされているのはそのためです。

#### モジュールの呼び出し（UI）

次にUI定義でモジュールを呼び出している箇所を見ると

```r
conditionalValueBoxUI("vb1")
```

および

```r
conditionalValueBoxUI("vb2")
```

のようにして、同じモジュール `conditionalValueBoxUI` を異なるID "vb1", "vb2" でそれぞれ呼び出しています。

#### モジュールの呼び出し（サーバー）

一方サーバー側におけるモジュールの呼び出し方は次のようになっています:

```r
server <- function(input, output) {
  callModule(conditionalValueBox, "vb1", reactive(input$value1))
  callModule(conditionalValueBox, "vb2", reactive(input$value2))
}
```

ここで `callModule()` 関数の第3引数として、入力値を `reactive(input$value1)` のように **reactiveな表現式** にしてから渡しています。これがモジュールを定義した関数 `conditionalValueBox(input, output, session, value)` の第4引数 `value` として利用されます。

## 参考

* valueBoxの基本用法については[こちら](https://rstudio.github.io/shinydashboard/structure.html#valuebox)の公式ドキュメントに書かれています。
* モジュールに値を渡す際にreactiveな表現式を使うことについては[以前の記事](https://qiita.com/terashim/items/097e0051db479e33ff24)で紹介しました。

Shiny Dashboardのより基本的な内容については

* [Shiny Dashboard](https://rstudio.github.io/shinydashboard/) （公式ページ）

が英語ながらわかりやすく書かれています。

Shiny modulesについては

* [Shiny - Modularizing Shiny app code](http://shiny.rstudio.com/articles/modules.html) 
* [「ShinyModule」で中規模Shinyアプリをキレイにする - Dimension Planet Adventure 最終章 最終話『栄光なる未来』](https://ksmzn.hatenablog.com/entry/shiny-module)

などが、reactiveについては

* [Shiny - Use reactive expressions](https://shiny.rstudio.com/tutorial/written-tutorial/lesson6/)
* [RStudio Shiny チュートリアル レッスン6 reactiveな表現式の使用 - Qiita](https://qiita.com/hoxo_m/items/eac52edc2ee59534415d)
* [Shinyを効率よく使うための、reactive関数基礎講座 - 世界一やさしいデータ分析教室](http://www.randpy.tokyo/entry/shiny_18)

などの解説があります。