マックブックな日々(Macbook Geek)

Retina Macbook Proで行った設定、アプリのインストール、トラブル対策等の覚書

ShinyがReactivityを実現する方法

参考:Tutorial: Building 'Shiny' Applications with R
Shinyの最大の特長が"Reactivity Programming"だ。Shinyのインタラクティビティは、
ユーザ入力>R言語>ウェブ出力
を自動的に実行することで実現されている。入力値や関数の変更を評価し、必要に応じてRコードを再実行するしくみがReactivityで、ShinyはReactivityライブラリを提供している。Reactivity Programmingでは、変更を陽にモニターする必要はない。

次のExampleコードを見てみる。
ui.R

library(shiny)

# Define UI for dataset viewer application
shinyUI(pageWithSidebar(

  # Application title
  headerPanel("Reactivity"),

  # Sidebar with controls to provide a caption, select a dataset, and 
  # specify the number of observations to view. Note that changes made
  # to the caption in the textInput control are updated in the output
  # area immediately as you type
  sidebarPanel(
    textInput("caption", "Caption:", "Data Summary"),

    selectInput("dataset", "Choose a dataset:", 
                choices = c("rock", "pressure", "cars")),

    numericInput("obs", "Number of observations to view:", 10)
  ),


  # Show the caption, a summary of the dataset and an HTML table with
  # the requested number of observations
  mainPanel(
    h3(textOutput("caption")), 

    verbatimTextOutput("summary"), 

    tableOutput("view")
  )
))

server.R

library(shiny)
library(datasets)

# Define server logic required to summarize and view the selected dataset
shinyServer(function(input, output) {

  # By declaring datasetInput as a reactive function we ensure that:
  #
  #  1) It is only called when the inputs it depends on changes
  #  2) The computation and result are shared by all the callers (it 
  #     only executes a single time)
  #  3) When the inputs change and the function is re-executed, the
  #     new result is compared to the previous result; if the two are
  #     identical, then the callers are not notified
  #
  datasetInput <- reactive(function() {
    switch(input$dataset,
           "rock" = rock,
           "pressure" = pressure,
           "cars" = cars)
  })

  # The output$caption is computed based on a reactive function that
  # returns input$caption. When the user changes the "caption" field:
  #
  #  1) This function is automatically called to recompute the output 
  #  2) The new caption is pushed back to the browser for re-display
  # 
  # Note that because the data-oriented reactive functions below don't 
  # depend on input$caption, those functions are NOT called when 
  # input$caption changes.
  output$caption <- reactiveText(function() {
    input$caption
  })

  # The output$summary depends on the datasetInput reactive function, 
  # so will be re-executed whenever datasetInput is re-executed 
  # (i.e. whenever the input$dataset changes)
  output$summary <- reactivePrint(function() {
    dataset <- datasetInput()
    summary(dataset)
  })

  # The output$view depends on both the databaseInput reactive function
  # and input$obs, so will be re-executed whenever input$dataset or 
  # input$obs is changed. 
  output$view <- reactiveTable(function() {
    head(datasetInput(), n = input$obs)
  })
})

ui.Rでは、

  • "caption"という名前の textInput
  • "dataset"という名前のselectInput
  • "obs”という名前のnumericInput

を定義している。


server.Rの先頭が、

shinyServer(function(input, output) {

となっている。関数に渡されるinput, output各オブジェクトがuiとのやり取りを媒介している。

inputオブジェクトはウェブページ上の入力をリスト形式で取得する。Reactivityは通常の関数を"reactive"関数に渡すことで実現する。下の例では、datasetInputをreactive関数として宣言している。

datasetInput <- reactive(function() {
   switch(input$dataset,
          "rock" = rock,
          "pressure" = pressure,
          "cars" = cars)
})

一方、出力はoutputオブジェクトを使う。下では、textinput "caption"への入力を、そのまま"caption"へ文字出力(reactiveText)している。

 output$caption <- reactiveText(function() {
    input$caption
  })

次では、datasetinputの再実行(つまり、input$datasetの変更)を見て、summary関数の出力を、outputオブジェクトの"summary"として表示(reactivePrint)している。

output$summary <- reactivePrint(function() {
    dataset <- datasetInput()
    summary(dataset)
  })

こちらは、datasetInputの再実行(つまり、input$datasetの変更)または、numericInput"obs"の変更が生じた場合に、outputオブジェクトの"view"が変更される(reactiveTable)。

output$view <- reactiveTable(function() {
    head(datasetInput(), n = input$obs)
  })