確率分布をさわれるShinyアプリ「確率分布Viewer」に新機能を追加しました!

私が運営中のShinyアプリ「確率分布いろいろ」ですが、 このたびアプリ名を「確率分布Viewer」に変更しました。

それに伴い、 新機能 として、

  • 日本語・英語切替

  • ブックマーク機能

を追加しました!
ぜひ遊んでみてください。

こちらからアクセス可能です!↓

確率分布Viewer - http://statdist.ksmzn.com デモ


それでは新機能をご紹介します。

新機能1: 日本語・英語切り替え機能

まず、「日本語・英語切り替え機能」 です。

より多くの方に使っていただけるように、英語対応しました。 もちろん、これまでの日本語表示も同様にお使いいただけます。

アプリを起動すると、最初は英語で表示します。 言語を切り替えるには、画面右上の「English」ボタンを押して、表示したい言語を選択します。

https://gist.githubusercontent.com/ksmzn/42cd12b4c21bbea8ac2e7871bec28aac/raw/258b2cac2b454e227f6ab3d99447383796e4c09d/pda_i18n.gif

試しに「Japanese」を選択にしてみると、日本語表示に切り替わりました。 もちろん、分布の形状は維持したままです。

現在対応しているのは日本語と英語の2つのみです。 他の言語に対応する予定は今の所ございませんが、 プルリクエスト いただけたら喜んで反映させていただきます。

この機能を作るにいたっては、shiny.i18n パッケージを使いました。 詳しく知りたいかたは、こちらの記事↓をご覧ください。

ksmzn.hatenablog.com

また、英語の文章や単語は、以前翻訳版アプリを作成していただいた @kaz_yosさんにご許可いただき、 こちらにも反映いたしました。 元の英語版はこちらから見ることができます。 @kaz_yosさん、ありがとうございました。

新機能2: ブックマーク機能

shinyの標準機能であるブックマーク機能を搭載しました。

ブックマーク機能とは、Shinyアプリの状態を保存・復元できる機能です。 この機能については後日ブログにまとめる予定なので、ここでは「確率分布Viewer」アプリでの使い方を記します。

例えば、ポアソン分布の画面を開き、適当にパラメータを変更します。 次に、右上の 「Bookmark」 ボタンを押すと、 分布の状態がURLのクエリパラメータとして保存されます!

https://gist.githubusercontent.com/ksmzn/42cd12b4c21bbea8ac2e7871bec28aac/raw/258b2cac2b454e227f6ab3d99447383796e4c09d/pda_bookmark_01.gif

このURLをコピーし、新しいタブやウィンドウのURL欄に入力して開いてみると、 保存した状態の分布のカタチがそのまま復元できます!

https://gist.githubusercontent.com/ksmzn/42cd12b4c21bbea8ac2e7871bec28aac/raw/258b2cac2b454e227f6ab3d99447383796e4c09d/pda_bookmark_02.gif

もちろん、他の人に共有することも可能です。 試しにこのURL ↓ にアクセスしてみてください。

http://statdist.ksmzn.com/?_inputs_&pois-lambda=2.5&selected_language=%22Japanese%22&pois-p_or_c=%22c%22&tabs=%22pois%22&pois-range=%5B0%2C14%5D

ポアソン分布の累積分布関数が表示されたと思います。

もちろん、日本語・英語を切り替えてもしっかり反映されます。 これで、分布の形状を同僚に伝えたいときに活用できますね! ブックマークボタンを魔改造してTweetボタンも付けましたので、是非Twitterにシェアしてみてください。

これから

このアプリを作ったのは3年以上も前なのですが、Shiny とその周辺技術はかなり進歩しており、 私が「こういうことできるかな?」と思ったことがほぼ実現できて驚きました。 (たくさん魔改造も入ってますが)

今回のBookmark機能もそうですし、前回記事でご紹介した「ShinyModule」機能も、 アプリのリファクタリングにかなり役立ちました。

機能が増えたせいか、アプリの起動がちょっと重くなったのが課題です。 プロファイリングなど頑張ります。 アドバイス等ありましたら是非お願いします。

これからもっと便利にするために、

  • 対応する確率分布の数を増やす(3次元とか良いですね)

  • 確率分布同士の関係性を見る機能を作る

ことを考えています。 趣味プロジェクトなのでゆっくりやりたいと思います。

アプリのソースコードは全てこちら↓から閲覧可能です。(レポジトリ名変更しました) 稚拙ですがご参考になれば幸いです。

github.com

皆様よろしくお願いします。

ほしいものリスト

「ShinyModule」で中規模Shinyアプリをキレイにする

こんにちは。今日はShiny力を高める記事です。

Shinyは便利ですが、だんだん規模が大きくなったときに、どうコードを書けば良いのか悩みます。 そこで今回は、Shinyコードが多くなってきたときに便利な機能 「ShinyModule」 について説明します。

ShinyModuleとは

概要

ShinyModuleとは、大きくなったShinyアプリを見通しよく書くための機能で、バージョン0.13.0から追加されました 。 その概念を大雑把に述べると、ちっちゃなShinyアプリのようなものです。 大きなShinyアプリから切り出して、関心ごとのShinyアプリ(のようなもの)に分けたのがShiny Moduleというイメージです。

小さなShinyModuleを組み合わせて大きなShinyアプリをつくることで、見通しよいコードとなります。

何が嬉しいのか

ShinyModuleは関数のように再利用することができます。そのため、似たようなパーツを複数持つShinyアプリを作る際に、似たようなコードをコピペせずに済みます。 複数タブで似たようなページを持つアプリで特に便利です。

ここまで聞くと「関数で良いだろ」と思うかもしれませんが、よりShinyに特化したものがShinyModuleというイメージですかね。

一番うれしいのは、名前空間をいい感じに管理してくれることです。

名前空間がやばい

みなさんは、Shinyアプリを作っていて、input・outputのIDが被りそうになった経験はありませんか? 例えば、複数のグラフを載せるアプリの場合、outputのIDを output$result1_plot , output$result2_plot , output$result3_plot ... として、管理が大変になったり。 さらに、各プロットの平均も載せようとして、 output$result1_mean , output$result2_mean , output$result3_mean ... としてしまうと、ますます混乱していきます。

そんなときこそ、ShinyModuleを使うときです。 まず、server.RからModuleとして切り離し、そのModuleの中で output$plot , output$mean という名前でserver側の処理を書きます。 次に、同様にui部分でもModuleとして切り離し、各グラフを識別する ID (この場合、 result1 , result2 , result3 ... )を記載するだけでOKです。 これだけで、result1 を渡したときは、アプリ全体の中では output$result1-plot , output$result1-mean として認識されるようになります。

書き方

基本

説明だけではイメージしにくいと思うので、実際に書いてみましょう。

ShinyModuleは、次のような型式で書きます。

# UI
irisPlotUI <- function(){ # UI }

# Server
irisPlot <- function(){ # Serverロジック }

UI部分のModuleとServer部分のModuleが必要で、UI側は末尾が Input , Output , UI のいずれかでなければなりません。 Server側はそれらの末尾文字列を消したものと同名である必要があります。

実例 〜 改修前 〜

では、これを踏まえ、irisデータのヒストグラムを作るShinyアプリを書いてみます。こんな感じです。

f:id:ksmzn:20180124215219p:plain

上側にPetal, 下側にSepalの、それぞれ Length か Width のヒストグラムを表示するアプリです。

ShinyModuleを使わずに素直に書くと、次のようなコードになりました。

# No Shiny Module
library(shiny)

ui <- fluidPage(
  selectInput('petal_col', '列:', c('Petal.Length', 'Petal.Width')),
  plotOutput('petal_plot'),
  selectInput('sepal_col', '列:', c('Sepal.Length', 'Sepal.Width')),
  plotOutput('sepal_plot')
)
server <- function(input, output, session) {
  output$petal_plot <- renderPlot({
    hist(iris[, input$petal_col])
  }) 
  output$sepal_plot <- renderPlot({
    hist(iris[, input$sepal_col])
  }) 
}

shinyApp(ui, server)

このくらいならわかりやすいですが、ui, serverそれぞれで、コピペしてしまっている箇所がありますね。 さらにプロットを増やしたくなると、煩雑なコードになるのが目に見えています。

これを、ShinyModuleを使って綺麗にしましょう。

Module UI

ShinyModule のUI側は、次のように書きます。

irisPlotUI <- function(id, cols) {
  ns <- NS(id)

  tagList(
    selectInput(ns('col'), '列:', cols),
    plotOutput(ns('plot'))
  )
}

id, colsを引数にとり、何らかのアウトプット(この場合はselectInputとプロット)を返します。 ShinyModuleの書き方のルールとして、第一引数は必ず id です。これが名前を識別するためのidとなります。 第二引数以降は、各Moduleで必要なものを追加できます。 この場合、selectInputに必要な選択肢として、cols を使うので追加しました。

通常のShinyと違うのは、NS の部分です。

NS 関数は、識別するための文字列を渡すと、Namespace(名前空間)を管理してくれる関数を返します。

> ns <- shiny::NS('result1')
> ns('plot')
[1] "result1-plot"

名前空間を管理といっても、識別文字列を先頭に追加するだけです。 単純ですが、これを使って名前が被らないようにしています。

Module Server

次に、これに対応するServer側の書き方を見てみましょう。

irisPlot <- function(input, output, session, main){
  output$plot <- renderPlot({
    hist(iris[, input$col], main = main)
  }) 
}

input, output, sessionを引数にとるところまでは、通常のShinyのserverと同じです。 さらに、追加引数として、そのmodule特有の引数をとることもできます。 この場合、ヒストグラムのタイトルとして main という引数を追加しています。

お気づきのとおり、単純にoutputのIDを plot と書くことができます。 ShinyModuleを使わない例のように、わざわざ output$petal-plot , output$sepal-plot などと書き分ける必要がありません。 便利ですね。

Module を組み込む

では、これらのModuleを、ui, serverに組み込みましょう。

まず、uiは次のように書きます。

ui <- fluidPage(
  irisPlotUI('petal', c('Petal.Length', 'Petal.Width')),
  irisPlotUI('sepal', c('Sepal.Length', 'Sepal.Width'))
)

単純に irisPlotUI を呼び出し、id と 選択肢を引数として渡します。 このidが、それぞれのModuleの間で識別するために使われます。

次に、server側です。

server <- function(input, output, session) {
  callModule(irisPlot, "petal", "Petal Plot")
  callModule(irisPlot, "sepal", "Sepal Plot")
}

server側の書き方は、少し特殊です。 callModule 関数の第一引数に Module名、第二引数にid, 第三引数以降にはそのModuleで必要なものを渡します。

実例 〜 改修後 〜

以上で改修終了です。最終的にこのようなコードになりました。

# Shiny Module
library(shiny)

irisPlotUI <- function(id, cols){
  ns <- NS(id)
  tagList(
    selectInput(ns('col'), '列:', cols),
    plotOutput(ns('plot'))
  )
}
irisPlot <- function(input, output, session, main){
  output$plot <- renderPlot({
    hist(iris[, input$col], main = main)
  }) 
}

ui <- fluidPage(
  irisPlotUI('petal', c('Petal.Length', 'Petal.Width')),
  irisPlotUI('sepal', c('Sepal.Length', 'Sepal.Width'))
)
server <- function(input, output, session) {
  callModule(irisPlot, "petal", "Petal Plot")
  callModule(irisPlot, "sepal", "Sepal Plot")
}

shinyApp(ui, server)

行数さえ増えたものの、見通しがよくなりました。

コアの機能部分が独立したModuleになっているので、ui, server内で無駄なコピペが減っています。 もっとプロットを増やしたくなっても、簡単に修正可能です。 さらに、名前管理も自前でやる部分が少なくなり、心理的負担も減りました。

終わりに

ShinyModuleによる見通しのよい書き方をご説明しました。 いきなりShinyModuleを使う必要はありませんが、アプリが大きくなり、処理を関数に分けたくなったときに検討する価値はあります。

僕が運営している 「ShinyDistributionsApp」 というアプリでは、ShinyModule部分を module.R という別のRファイルに書き、global.Rの中から読み込むようにしています。 こうすると、ui.R, server.Rが簡潔になるのでオススメです。 特にShinyDashboardを使う場合はui.R, server.Rが長くなりがちなので、是非試してみてください。 コードは こちら です。

参考文献

reactRパッケージでRからReactを実行する

このエントリは R Advent Calendar 2017 20日目の記事です。

こんにちは。 最近Shinyでちょっと複雑なことをやりたくてJavaScriptを書いていたのですが、 どうせなら最近よく書いている React を使いたいなと思っていたところ、 reactR というドンピシャなパッケージを見つけたので紹介します。

github.com

なぜReactなのか

ReactFacebook製のUIを構築するためのJavaScriptライブラリです。 シングルページアプリケーションを作るときなどによく採用され、近年人気を博しています。 詳しい解説はネット上にいくらでもあるので省略します。

少し複雑なことをやろうとするとjQueryはすぐに読みにくくなってしまいますが、Reactならば直感的に書けます。 また、React用に用意されているリッチなコンポーネントライブラリも増えてきており、例えばこれをShinyで使えれば、既存の用意されているもの以外にも使えるデザインの幅が広がります。

というわけで reactR をご紹介します。 ただ、このパッケージはまだ開発が十分でないことをご留意ください。

reactRの作者について

reactRパッケージの作者は、@timelyportfolio さんです。 この方は可視化や地理空間情報分析に長けており、RとJavaScriptのプロフェッショナルという貴重なスキルセットをお持ちな方です。

以前紹介した「mapedit」「mapview」もこの方が開発に関わっており、個人的に目が離せない人物です。

ksmzn.hatenablog.com

ksmzn.hatenablog.com

簡単な使い方

では、使ってみましょう。

library(reactR)
library(htmltools)

x <- tagList(
  reactR::html_dependency_react(),
  tags$div(id = "example"),
  tags$script(
    HTML(
      reactR::babel_transform("
        const Hello = <h1>Hello, ReactR!</h1>
        ReactDOM.render(
          Hello,
          document.getElementById('example')
        )
      ")
    )
  )
)
browsable(x)

f:id:ksmzn:20171220030341p:plain

上記を実行すると、図のように「Hello, ReactR!」と表示されます。 簡単に言えば、tag を駆使してHTMLの骨格を作り、reactRの関数でreactを読み込んだりreactで書かれたJavaScriptコードを挿入しています。

reactRが提供する関数はたった2つです。

html_dependency_react

reactの htmltools::htmlDependency を作ってくれる関数です。 これにより、外部ライブラリであるreactをcdnを用いて読み込むことができます。 さきほどのコードを実行してできた画面のソースをみると、しっかりreactが <script> タグに挿入されています。

babel_transform

babelを用いてコードを変換してくれる関数です。 これにより、ES2015やJSXを用いてコードを書くことができます。 特にJSXは、Reactを書くときに欠かせないのでありがたいですね。

ちなみに、この関数の内部では V8 パッケージが使われています。 V8パッケージは、Google製のJavaScript実行エンジンをRから呼び出すパッケージです。

日本語の記事だと yamano357さんが既に活用していますね 。さすがです。

サンプル

reactRのレポジトリにはいくつかサンプルがあります。 それを読むと、html_dependency_react の内部を参考にhtmlDependencyを定義すれば、外部パッケージを読み込めるようです。

ということは、冒頭に書いたように、React用に提供されているライブラリも使うことが可能です。

例えば、MicroSoftが提供しているReact用UIライブラリ「office-ui-fabric-react」を使ってみると、

library(htmltools)
library(reactR)

fabric <- htmlDependency(
  name = "office-fabric-ui-react",
  version = "5.23.0",
  src = c(href="https://unpkg.com/office-ui-fabric-react/dist"),
  script = "office-ui-fabric-react.js",
  stylesheet = "css/fabric.min.css"
)

browsable(
  tagList(
    html_dependency_react(),
    fabric,
    tags$div(id = "app-button"),
    tags$script(HTML(babel_transform(
      '
      const btn = 
        <Fabric.DatePicker placeholder="Select a date..." />

      ReactDOM.render(btn, document.querySelector("#app-button"));
      '
    )))
  )
)

f:id:ksmzn:20171220031005g:plain

このような感じで、ライブラリが提供しているDatePickerが使えるようになりました。

まとめ

サンプル少なすぎてすみません。 本当はもっとリッチなものをつくりたかったのですが、 いくつかエラーにハマり、載せるクオリティのものが作れないまま時間切れとなりました。 なにか作ったら追記しますが、その前に解決すべきエラーが多そうです。 この分野は少しマニアックではありますが、 なかなか面白いので、もう少し深堀を続けるつもりです。

使いこなせるようになったら自作Shinyアプリに載せたいですね。

そうそう、reactと同じくらい人気のフレームワークである「vue.js」が使えるRパッケージ、「vueR」もあるそうですよ。残念ながら僕はvueを書かないので使う見込みはなさそうですが、vue派の人はぜひどうぞ。

エンジョイ。