formattableでshinyに推定結果をオシャレに表示する

R Shiny Advent Calendar 2018 18日目です。

この記事では、統計分析した結果をshinyで簡単にイイ感じで表示する方法を紹介します。

# 実際の場面だとこんな感じ
LASSO回帰の結果を表示。
![WS000001.JPG](https://qiita-image-store.s3.amazonaws.com/0/189855/fa8f20bc-dc44-792d-3c26-c2d3eb4623ab.jpeg)
LASSO回帰で予測に使用されている箇所を太字、符号を色で表現することで
数表であっても結果が読みやすくなっています。

# formattableとは
dataframeに簡単にイイ感じの書式を設定してくれるパッケージ。
戻り値がhtmlなので、shinyとの相性が抜群です。

shinyでdataframeを表示するパッケージにDTもありますが、
結果表の出力にはformattableの方が便利です。

# formattableの機能整理
以下ではformattableの機能を順番に紹介していきます。
データとして、気象庁から日本の気温データを使用したいと思います。

[気象庁HP](https://www.data.jma.go.jp/cpdinfo/temp/)

## データの準備

```{r}
library(tidyverse)
library(magrittr)
library(formattable)

raw <- read_csv(
  'https://www.data.jma.go.jp/cpdinfo/temp/list/csv/mon_jpn.csv',
  locale=locale(encoding="CP932"))

raw %<>% filter(年 >= 2014) # 直近5年分に絞り込み

# 時系列の持ち方を変形
raw %<>% gather(month, temp, -年) %>% 
  mutate(month = factor(month, levels=unique(month), ordered=T)) %>% 
  spread(年, temp)
```

## まずはそのまま出力

```{r}
formattable(raw)
```
前述の通りhtmlに出来ますが、ここでは画像で掲載します。
<img src=https://qiita-image-store.s3.amazonaws.com/0/189855/5c57e234-43f8-b45e-7431-ab2a43e8d9bc.jpeg width=50%>
このままで十分使えるなのでは...という印象です。
ただ、そもそも表って、作った人が思っているより伝わりにくいものです。
[Gelman先生の名言](https://andrewgelman.com/2011/12/22/tables-as-graphs-the-ramanujan-principle/)
そこで、以下で書式を設定していきます。

## 太字、文字色
もちろんできます。
formattableでは、基本的に

**列名 = formatter**

で書式の名前付きリストを作ることで、書式を設定していきます。

```{r}
# 太字、文字色 ----
raw %>% formattable(
  list(
    month = formatter("span", style=style(
      font.weight="bold",
      color="orange"
      ))
    )
  )
```
<img src=https://qiita-image-store.s3.amazonaws.com/0/189855/18ac9082-b02a-1b40-aa6d-66862dd342ed.jpeg width=50%>
とても簡単ですね。

## ヒートマップ的なアレコレ
excelでおなじみのカラースケールやデータバーですね。
color_tableやcolor_barをformatterの代わりに渡します。
color_barは負値があると挙動が怪しいので、normalize_barを使います。

```{r}
# color_tile, color_bar
raw %>% formattable(
  list(
    `2014` = color_tile("transparent", "#71CA97"),
    `2015` = normalize_bar("lightblue")
  )
)
```
<img src=https://qiita-image-store.s3.amazonaws.com/0/189855/36451f5f-af56-d306-deea-eb18adffb263.jpeg width=50%>
お手軽にexcel likeな書式設定が可能です。
## area
これまでの書式だと、複数列に書式を適用するのがとても面倒です。
そもそもshinyは動的な処理があってこそなので、列名が事前にわかっているとは限りません。
そんなときに**area**を使って処理します。

基本的な文法は
**area(row, col) ~ formatter**
です。

```{r}
# area ----
raw %>% formattable(
  list(
    area(col = `2014`:`2018`) ~ color_tile("transparent", "lightblue"),
    month = formatter("span", style=style(font.weight="bold"))
  )
)
raw %>% formattable(
  list(
    area(row = 1:6  ,col = 4:6) ~ formatter("span", style=style(font.weight="bold", color="steelblue")),
    area(row = 7:12  ,col = 2:4) ~ formatter("span", style=style(font.weight="bold", color="orange")),
    month = formatter("span", style=style(font.weight="bold"))
  )
)
```
<img src=https://qiita-image-store.s3.amazonaws.com/0/189855/423c6ca3-0dfa-122b-f446-33b19bcb4cb5.jpeg width=45%>   <img src=https://qiita-image-store.s3.amazonaws.com/0/189855/ee3b3bcb-d9d7-eb5a-bef8-b0ad70731997.jpeg width=45%>
こんな感じで、手軽に範囲に対しても書式設定を行うことができます。
※ただし、同一セルに複数の書式を適用するのは結構難しいので注意。
## カスタマイズ系
たいていの統計的な結果は、条件付き書式、つまりif条件で適用するべきものが多いかと思います。
気温の例でいうと、

* 「平均気温より高いか低いか（気温偏差の正負）」
* 「極端な気温があったのはどこか（１偏差以上のずれがあったのはどこか）」

これをformattableで書いてみます。
基本的にはifelseやcase_whenで書くことができます。

```{r}
# if文
raw %>% formattable(
  list(
    `2014` = formatter(
      "span",
      style = x ~ style(color = ifelse(x > 0, "green", "red"),
                        font.weight = ifelse(abs(x) > 1, "bold", NA)))
  )
) 
```
<img src=https://qiita-image-store.s3.amazonaws.com/0/189855/3187c500-4687-6a9c-7b3f-4459b0a9267e.jpeg width=60%>
formatterの中でローカル変数を挟むのがコツです。
私の周囲の冬モノを売る事業者の方はかなり大変そうにしていましたが、
これをみると、2018年の11月はかなり暖かったみたいです。

もちろん複数列に対する条件も書くことができます。
2017年から気温が増加したか、減少したかで色を付けてみましょう。

```{r}
raw %>% formattable(
  list(
    `2018` = formatter(
      "span",
      style = ~ style(color = ifelse(`2018` > `2017`, "green", "red"))
    )
  )
)
```
<img src=https://qiita-image-store.s3.amazonaws.com/0/189855/f0597864-e395-13ce-2efb-88931b9b1d4b.jpeg width=50%>
とても楽チンですね。

## icontext
色だけでなく、アイコンが付いていると、表が華やかになります（本質的ではないが...）。
icontextを使うと、bootstrap glyphiconsのアイコンを使用することができます。
[Components](https://getbootstrap.com/docs/3.3/components/)
矢印と符号ぐらいしか使いませんが...

```{r}
raw %>% formattable(
  list(
    area(col=`2014`:`2017`) ~ formatter(
      "span",
      style = x ~ style(color = ifelse(x > 0, "green", "red"),
                        font.weight = ifelse(abs(x) > 1, "bold", NA))),
    `2018` = formatter(
      "span",
      style = x ~ style(color = ifelse(x > 0, "green", "red"),
                        font.weight = ifelse(abs(x) > 1, "bold", NA)),
      ~ icontext(ifelse(`2018` > `2017`, "arrow-up", "arrow-down"), `2018`)
      )
    )
  )
```
<img src=https://qiita-image-store.s3.amazonaws.com/0/189855/cf354642-b903-16e0-0cc9-253ea047b6fb.jpeg width=60%>
たった1行でアイコンが追加できるのは嬉しいですね。

# shinyでの実装
formattableには、ui.Rで使う**formattableOutput**とserver.Rで使う**renderFormattable**が用意されているので、これを使うだけのお手軽実装です。

```{r}
library(shiny)
library(tidyverse)
library(magrittr)
library(formattable)


ui <- fluidPage(
  
  titlePanel("formattable"),
  
  fluidRow(
    column(8,
           formattableOutput("table")
    )
  )
)

server <- function(input, output) {
  
  raw <- read_csv(
    'https://www.data.jma.go.jp/cpdinfo/temp/list/csv/mon_jpn.csv',
    locale=locale(encoding="CP932"))
  
  raw %<>% filter(年 >= 2014) # 直近5年分に絞り込み
  
  # 時系列の持ち方を変形
  raw %<>% gather(month, temp, -年) %>% 
    mutate(month = factor(month, levels=unique(month), ordered=T)) %>% 
    spread(年, temp)
  
  output$table <- renderFormattable(
    # icontext ----
    raw %>% formattable(
      list(
        month = formatter("span", style=style(font.weight="bold")),
        area(col=`2014`:`2017`) ~ formatter(
          "span",
          style = x ~ style(color = ifelse(x > 0, "green", "red"),
                            font.weight = ifelse(abs(x) > 1, "bold", NA))),
        `2018` = formatter(
          "span",
          style = x ~ style(color = ifelse(x > 0, "green", "red"),
                            font.weight = ifelse(abs(x) > 1, "bold", NA)),
          ~ icontext(ifelse(`2018` > `2017`, "arrow-up", "arrow-down"), `2018`)
        )
      )
    )
  )
}
shinyApp(ui = ui, server = server)
```
<img src=https://qiita-image-store.s3.amazonaws.com/0/189855/dcd8ca0e-97f3-b030-5708-5868c9783893.jpeg width=70%>

# おわりに
formattableを使うことで、読みやすい表が簡単に作ることができます。

shinyの使用用途は色々だと思いますが、特にRが得意な統計的処理を行うためのインターフェースとして用いるなら、結果は数表となることも多いかと思います。

shinyなら入出力はサクッと仕上げ、メインの統計処理やデバッグに時間を使うことが出来るのが魅力だなあと感じています。

# 今後
カレンダーに余裕があれば、統計的因果推論をshinyで実装した話を書こうと思います。