R
Shiny
shinydashboard

R - ShinyによるWebアプリケーション作成: shinydashboard編

More than 1 year has passed since last update.

はじめに

システムの稼働状況とかミドルウェアの統計情報とかを手っ取り早く可視化するためにRを使ってみようということで書き始めた連載企画です。
Rでは、集計結果をサクッとWebアプリケーションとして作成するためのShinyというステキなパッケージが提供されています。Shinyを使えば、Webアプリケーションの知識があまり無くても、割と簡単にWebアプリが作れて、きれいなWebページでRによる集計結果を表示させることができます。
今回はshinydashboard編です。

関連記事

インフラ屋さんのためのR言語: 環境構築編
オフラインでのR環境構築 on RHEL
z/OSにRを導入してみた
インフラ屋さんのためのR言語: プログラミング編
R Markdownによるレポート生成
R MarkdownのHTMLレポートをブラッシュアップ
R - ShinyによるWebアプリケーション作成: 基礎編
R - ShinyによるWebアプリケーション作成: shinydashboard編 <= 当記事

shinydashboard

ShinyでWebアプリを作る場合に強くお勧めしたいのがshinydashboardというパッケージの利用です。Shinyチュートリアルを見て頂くと分かりますが、Shiny単体ではui.R/server.R で単発のWebページが作れるだけです。関連性のある複数のアプリ(ページ)をまとめたり行ったり来たりしたりしたいような場合、それらをまとめるトップページのようなものを用意しなければなりません。
Shiny Dashboardを使うと、いわゆるダッシュボードのようなページやポータル的なサイトが簡単に作れます。
shinydashboard

shinydashboardを使うと以下のような画面構成のWebアプリが作れます。
image01.JPG

Sidebarからメニューを選ぶとそれに応じた内容がBody部分に表示されるイメージです。

ファイル構造

shinydashboardでは、ダッシュボードっぽいレイアウトが作れるとはいえ、ベースの考え方はshinyなので、ui.R, server.Rで管理されるというのは同じです。ui.Rのレイアウトを作る際に、上のようなダッシュボードやポータルっぽいサイトが作りやすいような機能が各種提供されているイメージです。
なので、普通にコードを書いていくと、ui.Rやserver.Rに、複数画面(複数のBody)に関する情報が全て保持されることになり、管理が煩雑になりがちです。ページ毎にファイルを分けて管理すると分かりやすいと思います。

例えば、以下のような階層/カテゴリ分けでページを作ることを想定します(例なのでメニューは適当です)。

  • Information
  • Peformance Analytics
    • CPU
    • Memory
    • Disk
  • Admin
    • MachineList
    • Config

普通にui.Rを書くと、こんなイメージになります。

ui.R
dashboardPage(
  dashboardHeader(title = "Shiny Dashboard"),
  dashboardSidebar(
    sidebarMenu(
      menuItem("Information", icon=icon("info"), tabName = 'tab_Info'
      ),
      menuItem("Perf Analytics", icon=icon("line-chart"), 
               menuSubItem("CPU", tabName = "tab_CPU"),
               menuSubItem("Memory", tabName = "tab_Memory"),
               menuSubItem("Disk", tabName = "tab_Disk")),
      menuItem("Admin", icon=icon("gear"),
               menuSubItem("MachineList", tabName = "tab_MachineList"),
               menuSubItem("Config", tabName = "tab_Config")
      )
    )
  ),
  dashboardBody(
    tabItems(
      tabItem("tab_Info",
              h2("Information"),
              div(img(src="bigorb.png"),align="center")),
      tabItem("tab_CPU",
              h2("CPU Usage"),
              ### Layout for CPU tab ###
              p("...")

      ),

      tabItem("tab_Memory",
              h2("Memory Usage"),
              ### Layout for Memory tab ###
              p("...")

      ),
      tabItem("tab_Disk",
              h2("Disk Usage"),
              ### Layout for Disk tab ###
              p("...") 

      ),
      tabItem("tab_MachineList",
              h2("MachineList"),
              ### Layout for MachineList tab ###
              p("...")

      ),
      tabItem("tab_Config",
              h2("Config"),
              ### Layout for Config tab ###
              p("...")

      )
    )
  ),
  skin="blue"
)

画面イメージ
image02.JPG

ソースの構造を見てみます。
image03.JPG

このように、ui.Rに複数の画面のレイアウト情報が並ぶことになります。このレイアウト部分を画面単位にファイル分割してみると、ui.Rは以下のようになります。

ui.R
source('ui_Info.R', local = TRUE)
source('ui_CPU.R', local = TRUE)
source('ui_Memory.R', local = TRUE)
source('ui_Disk.R', local = TRUE)
source('ui_MachineList.R', local = TRUE)
source('ui_Config.R', local = TRUE)

dashboardPage(
  dashboardHeader(title = "Shiny Dashboard"),
  dashboardSidebar(
    sidebarMenu(
      menuItem("Information", icon=icon("info"), tabName = 'tab_Info'
      ),
      menuItem("Perf Analytics", icon=icon("line-chart"), 
               menuSubItem("CPU", tabName = "tab_CPU"),
               menuSubItem("Memory", tabName = "tab_Memory"),
               menuSubItem("Disk", tabName = "tab_Disk")),
      menuItem("Admin", icon=icon("gear"),
               menuSubItem("MachineList", tabName = "tab_MachineList"),
               menuSubItem("Config", tabName = "tab_Config")
      )
    )
  ),
  dashboardBody(
    tabItems(
      tabItem_Info,

      tabItem_CPU,
      tabItem_Memory,
      tabItem_Disk,

      tabItem_MachineList,
      tabItem_Config

    )
  ),
  skin="blue"
)

各ページのレイアウトはtabItemオブジェクトを作成してtabItem_xxxという変数に設定することで定義し、ui_xxx.Rというファイルで作成します。

ui_Info.R
tabItem_Info <- tabItem("tab_Info",
                        h2("Information"),
                        div(img(src="bigorb.png"),align="center")

)
ui_CPU.R
tabItem_CPU <- tabItem("tab_CPU",
                        h2("CPU Usage"),
                        ### Layout for CPU tab ###
                        p("...")

)

※他のファイルも同様です。

構造は以下のようなイメージです。
image04.JPG

このように、source('<ファイル名>', local=TRUE)で別ファイルをその場所に読み込むことができるので、ファイルを分割して管理することができます。


server.Rも同様に、機能ごとにファイル分割すると管理しやすいと思います。
以下のようなイメージになります。
image05.JPG


さらに、いくつかファイルを分けておくとよいものがあります。
R - ShinyによるWebアプリケーション作成: 基礎編で紹介した通り、基本はui.R, server.Rファイルを作成することになりますが、global.Rという共通で参照される変数などはこのファイルに書くことができます。なので、global.Rにライブラリーのロード(library())や共通の環境変数を指定しておくのがよいでしょう。
さらに、shinyは日本語環境が弱く、どうやらglobal.R, ui.R, server.R,これらのファイルの中に日本語が含まれているとうまく動作しないことがあります。普通にRStudioからテストのためにRunAppボタンを押してShinyアプリを起動させようとする場合でも、上の3つのファイルに日本語が含まれているとエラーになってうまく起動できない事象に遭遇しています(コメント部分に日本語が含まれていてもNGなことがある)。
ちょうど以下のような記事もありました。そのうち改善されることを期待しましょう。

Shinyapps.io にUTF8の文字列(日本語)を利用したアプリをデプロイする

デプロイの際に走るlint()がマルチバイトを受け付けず、それがライブラリのインストールを止めてしまいます。

ちなみに、global.R, ui.R, server.Rファイルに直接日本語を含めるのではなく、先に示したように別ファイルに定義したものをsource()で取り込めば回避できました。ですので、global.R, ui.R, server.Rそれぞれで日本語を扱いたいものがあった場合、それは別ファイルとして定義してあげるとよいです。例えば、global_env.R, ui_env.R, server_env.Rというようなファイルを用意し、それぞれsourceで取込むというようなことをしています。

まとめると、以下のようなファイル構造で管理するとよいのではないかと考えています。

global.R: パッケージのロード、ui.R, server.Rの共通の変数/関数定義など
global_env.R: global.Rに含めたい日本語を含む変数/関数定義など
ui.R: 全体の枠などの基本レイアウト
ui_env.R: ui.Rに含めたい日本語を含む変数/関数定義など
server.R: 全画面で共通の関数など
server_env.R: server.Rに含めたい日本語を含む変数/関数定義など
ui_xxx.R: 画面ごとのレイアウト
server_xxx.R: 画面ごとのロジック(render関数など)

ファイル分割時の注意点

上のように、ファイルを画面単位に分割して管理したとしても、結局はui.R, server.Rに統合されることになるので、1つのshinyアプリケーションであることに変わりはありません。すなわち、変数名やオブジェクトの名前はバッティングしないように管理してあげる必要があります。
ファイルが別になるとこの辺の管理が煩雑になりやすいのでどちらがよいかトレードオフなところがあるのですが...。
個人的には、変数名や関数名の付け方にルールを設けて管理するようにしています。
まず、各ページはユニークな名前を付けるようにします。先の例だとCPU, Memory, Disk などです。ファイル名やタブ名はこの名前をベースに付けます(ui_CPU.RやtabItem_CPUなど)。
さらに、各ページ固有の変数名や関数名にもプレフィックスとしてこのページユニークな名前を付けるようにします。例えば、通常であればdfPerfDataという名前でデータフレームを定義したいという場合、dfPerfData_CPUといったネーミングにします。これで似たような機能のオブジェクトがあってもページ毎に固有の名前が付くのでバッティングが避けられます。