shiny × backpipeは鬼に金棒
backpipeパッケージという便利なものを見つけたので、備忘録としてまとめておきたいと思います。
目次
backpipeとは?
簡単に言うとパイプ%>%
とは逆に後ろから前の順に関数を実行していく二項演算子%<%
を提供してくれるパッケージのことです。
ここまで読んで、「なるほど」と思った方はもう使えます。
パイプ?
まず、パイプ%>%
のお話から。
tidyverseを普段から使っている人にとって%>%
は必要不可欠ですよね。
直感的に思考の流れに沿って書けますし、理解しやすく、デバッグもしやすい書き方が自然とできると思います。
超簡単な例。
# install.packages("tidyverse") # パッケージを入れていない方はこちら library(tidyverse) # library(dplyr)でも良い tasu <- function(x, y) x+y kakeru <- function(x, y) x*y 1 %>% tasu(2) %>% kakeru(3) # (1+2)*3 1 %>% kakeru(3) %>% tasu(2) # (1*3)+2 iris %>% filter(Species == "setosa") %>% select(starts_with("Sepal"), Species) # irisのデータフレームを # Species == "setosa"の行に絞り # "Sepal"で始まる列とSpeciesの列にしぼる
わかりやすい。
※tidyverseについてはここでは触れないので、別途ググってみてください
backpipeの場合
上のプログラムをbackpipeで書くと
# 1 %>% tasu(2) %>% kakeru(3)が kakeru(3) %<% tasu(2) %<% 1 # (1+2)*3 # 1 %>% kakeru(3) %>% tasu(2)が tasu(2) %<% kakeru(3) %<% 1 # (1*3)+2 # iris %>% # filter(Species == "setosa") %>% # select(starts_with("Sepal"), Species)が select(starts_with("Sepal"), Species) %<% filter(Species == "setosa") %<% iris # irisのデータフレームを # Species == "setosa"の行に絞り # "Sepal"で始まる列とSpeciesの列にしぼる
「いつ使うんだよ、こんなの」と思うかもしれません。
じゃあ、いつ使うか。
shinyとは?
Rには shinyパッケージというものがあって、
リンク先の言葉を引用すると
Shiny is an R package that makes it easy to build interactive web apps straight from R.
(shinyはRパッケージで、ボタンをポチポチしただけで動かせるwebアプリを簡単に作成することができます)
と書いてあります。
大事なことなので、2回言いました。
百聞は一見に如かず
百聞は一見に如かずということで、作ってみましょう。
一番簡単にやる方法は、RStudioから
[File]→[New Project]→[New Directory]→[Shiny Web Application]→ディレクトリ名を指定
を実行する方法です。
実行すると、app.Rが作成されると思いますが、それがwebアプリの本体となります。
本体は大きく分けて2つのかたまりに分かれていて、以下の2つが含まれています。
- ui:User Interface、見た目を決めるプログラム
- server:(ボタンを押したときの動きなどの)処理全般を司るプログラム
では、実際にそのプログラムの中身を見てみます。
# # This is a Shiny web application. You can run the application by clicking # the 'Run App' button above. # これはShiny webアプリだよ。上にある"Run App"ボタンをクリックすると、アプリを起動できるよ。 # # Find out more about building applications with Shiny here: # # http://shiny.rstudio.com/ # library(shiny) # Define UI for application that draws a histogram # ヒストグラムを描くアプリのUIを定義する ui <- fluidPage( # Application title titlePanel("Old Faithful Geyser Data"), # Sidebar with a slider input for number of bins sidebarLayout( sidebarPanel( sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30) ), # Show a plot of the generated distribution mainPanel( plotOutput("distPlot") ) ) ) # Define server logic required to draw a histogram # ヒストグラムを描くために必要な処理を定義する server <- function(input, output) { output$distPlot <- renderPlot({ # generate bins based on input$bins from ui.R x <- faithful[, 2] bins <- seq(min(x), max(x), length.out = input$bins + 1) # draw the histogram with the specified number of bins hist(x, breaks = bins, col = 'darkgray', border = 'white') }) } # Run the application # アプリ実行! shinyApp(ui = ui, server = server)
UI作って、処理書いて、実行してるだけなので、構造としてはシンプルですね。
uiオブジェクトの中身を見てみる
ui, serverのオブジェクトのうちuiに注目してみてみます。
uiは下記のようなHTMLコードの文字列を持っています。
> ui <div class="container-fluid"> <h2>Old Faithful Geyser Data</h2> <div class="row"> <div class="col-sm-4"> <form class="well"> <div class="form-group shiny-input-container"> <label class="control-label" for="bins">Number of bins:</label> <input class="js-range-slider" id="bins" data-min="1" data-max="50" data-from="30" data-step="1" data-grid="true" data-grid-num="9.8" data-grid-snap="false" data-prettify-separator="," data-prettify-enabled="true" data-keyboard="true" data-data-type="number"/> </div> </form> </div> <div class="col-sm-8"> <div id="distPlot" class="shiny-plot-output" style="width: 100% ; height: 400px"></div> </div> </div> </div>
これを、Rコードと対応させてみると以下のようになります。
<div class="container-fluid"> # <- fluidPage() <h2>Old Faithful Geyser Data</h2> # <- titlePanel() <div class="row"> <div class="col-sm-4"> # <- sidebarPanel() <form class="well"> <div class="form-group shiny-input-container"> # <- sliderInput() <label class="control-label" for="bins">Number of bins:</label> <input class="js-range-slider" id="bins" data-min="1" data-max="50" data-from="30" data-step="1" data-grid="true" data-grid-num="9.8" data-grid-snap="false" data-prettify-separator="," data-prettify-enabled="true" data-keyboard="true" data-data-type="number"/> </div> </form> </div> <div class="col-sm-8"> # <- mainPanel() <div id="distPlot" class="shiny-plot-output" style="width: 100% ; height: 400px"></div> # <- plotOutput() </div> </div> </div>
uiを作り出すコードで出てくる関数の順番にはなっているが、
それゆえに、関数が実行される順番にはなっていないということがわかります。
では、この状態でパイプを使って簡潔に書けるかやってみます。
uiを作り出すコードの関数部分だけ取り出して書くと
fluidPage( titlePanel, sidebarLayout( sliderInput %>% sidebarPanel, # わかりづらい plotOutput %>% mainPanel # わかりづらい ) )
カッコの数が減って構成要素の塊を判別しやすくなりましたが、
パイプ%>%
を書いたことによって直観的な順番と逆になってしまいました。
ここでbackpipeの出番です。
backpipeで書き換え
> fluidPage( + + # Application title + titlePanel("Old Faithful Geyser Data"), + + # Sidebar with a slider input for number of bins + sidebarLayout( + sidebarPanel %<% + sliderInput("bins", + "Number of bins:", + min = 1, + max = 50, + value = 30), + + # Show a plot of the generated distribution + mainPanel %<% + plotOutput("distPlot") + ) + ) <div class="container-fluid"> <h2>Old Faithful Geyser Data</h2> <div class="row"> <div class="col-sm-4"> <form class="well"> <div class="form-group shiny-input-container"> <label class="control-label" for="bins">Number of bins:</label> <input class="js-range-slider" id="bins" data-min="1" data-max="50" data-from="30" data-step="1" data-grid="true" data-grid-num="9.8" data-grid-snap="false" data-prettify-separator="," data-prettify-enabled="true" data-keyboard="true" data-data-type="number"/> </div> </form> </div> <div class="col-sm-8"> <div id="distPlot" class="shiny-plot-output" style="width: 100% ; height: 400px"></div> </div> </div> </div>
先ほどの出力結果と一致し、なおかつ、直観的な順番になりました。
ただ、2項演算子なので、
> sidebarLayout %<% + list( + sidebarPanel %<% + sliderInput("bins", + "Number of bins:", + min = 1, + max = 50, + value = 30), + + mainPanel %<% + plotOutput("distPlot") + ) sidebarLayout(.) でエラー: 引数 "mainPanel" がありませんし、省略時既定値もありません
2つ以上の引数を一気にこのようにlist形式で渡したとしても、
引数が足りませんと怒られます。
HTMLの構成要素が切り分けられれば十分キレイに見えるし、
デバッグもしやすいので、このままでOKだと思います。
まとめ
今回の例だと恩恵が少ないように見えますが、
shinyで難しいカスタマイズをしていきたいときにはかなり役立ちます。
例)
div(class="outer-outer", div( class="outer", div( class="inner", h1( "content", role="heading" ) ) ) )
↓
div( class="outer-outer") %<% div( class="outer") %<% div( class="inner") %<% h1( "content", role="heading" )
shinyの詳しい書き方については最近、日本語の本も出ましたし、
興味のある方は下記の本を要チェックです!!
- 作者: 梅津雄一,中野貴広
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2018/11/07
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る