大家可能看了標題會有些不懂,這邊我所指的分段處理意思是,有時候你的原始資料並不是很完善,要先做流程A 產生一定的結果,再去作流程B 去分析流程,或者是流程A 是一個非常費時的計算,你只需要執行A一次,之後再去做其他重覆跑的分析,說到底,我今天想介紹的就是Button 元件。
剛好,官方文件有一篇就是專門在講按鈕,我就用這篇為大家導讀。
Pattern 1 - Command
這是最直覺的使用方式,在ui 那邊宣告好按鈕後,直接在server 這邊寫監聽的事件,observeEvent 會去等待是否有按鈕的狀態改變了,而執行後面的程式碼。
library(shiny)
ui <- fluidPage(
  tags$head(tags$script(src = "message-handler.js")),
  actionButton("do", "Click Me")
)
server <- function(input, output, session) {
  observeEvent(input$do, {
    session$sendCustomMessage(type = 'testmessage',
      message = 'Thank you for clicking')
  })
}
shinyApp(ui, server)
Pattern 2 - Delay reactions
這個用法可以達到react 的效果,這邊直接看程式碼解釋比較清楚,因為有宣告繪圖的地方,所以一開始就會畫一次hist ,而randomVals() 為一個等待button 的觸發事件,因此只有點擊按鈕時才會執行。
library(shiny)
ui <- fluidPage(
  actionButton("go", "Go"),
  numericInput("n", "n", 50),
  plotOutput("plot")
)
server <- function(input, output) {
  randomVals <- eventReactive(input$go, {
    runif(input$n)
  })
  output$plot <- renderPlot({
    hist(randomVals())
  })
}
shinyApp(ui, server)
Pattern 3 - Dueling buttons
使用情境為同一事件可被兩個按鈕所觸發,先看reactiveValues 部分,他宣告了v 為react 元件,初始資料data 是null ,然後兩顆按鈕透過observeEvent 來改變v的data ,因此只要data 的狀態改變了,就會再次觸發renderPlot 去畫新的圖。
library(shiny)
ui <- fluidPage(
  actionButton("runif", "Uniform"),
  actionButton("rnorm", "Normal"), 
  hr(),
  plotOutput("plot")
)
server <- function(input, output){
  v <- reactiveValues(data = NULL)
  observeEvent(input$runif, {
    v$data <- runif(100)
  })
  observeEvent(input$rnorm, {
    v$data <- rnorm(100)
  })  
  output$plot <- renderPlot({
    if (is.null(v$data)) return()
    hist(v$data)
  })
}
shinyApp(ui, server)
另外,這邊要注意的是renderPlot 第一次的執行中必須要有使用到v$data,之後的react 才會成立,如果你寫成
output$plot <- renderPlot({
        if (round(runif(1)) %% 2 == 0) return()
        hist(v$data)
    })
然後亂數round(runif(1))剛好是0 直接return 出去,那麼就算你改變了v$data ,renderPlot也不會執行!
Pattern 4 - Reset buttons
其實和Patter 3 差不多,只是改成對data 設為null,使renderPlot 時直接return() 出去,就會有清空圖表的效果。
library(shiny)
ui <- fluidPage(
  actionButton("runif", "Uniform"),
  actionButton("reset", "Clear"), 
  hr(),
  plotOutput("plot")
)
server <- function(input, output){
  v <- reactiveValues(data = NULL)
  observeEvent(input$runif, {
    v$data <- runif(100)
  })
  observeEvent(input$reset, {
    v$data <- NULL
  })  
  output$plot <- renderPlot({
    if (is.null(v$data)) return()
    hist(v$data)
  })
}
shinyApp(ui, server)
Pattern 5 - Reset on tab change
這是文章裡最複雜的模式,ui 分了兩個tab 分頁,並指定元件id 為tabset,server 部分依樣指定v 為reactiveValues,然後監聽Plot 按鈕,只要一但按下,input$go 就會+1 ,另外也監聽tabset 是不是有切換分頁的行為,若有則將v$doPlot 設為0,這麼一來v$doPlot 只有在沒按過按鈕和切換分頁時會是false,最後在renderPlot 裡面可以看到isolate 函式,它可以將括號內程式碼脫離react,除去不該重覆跑的情況。
library(shiny)
ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      tabsetPanel(id = "tabset",
        tabPanel("Uniform",
          numericInput("unifCount", "Count", 100),
          sliderInput("unifRange", "Range", min = -100, max = 100, value = c(-10, 10))
        ),
        tabPanel("Normal",
          numericInput("normCount", "Count", 100),
          numericInput("normMean", "Mean", 0),
          numericInput("normSd", "Std Dev", 1)
        )
      ),
      actionButton("go", "Plot")
    ),
    mainPanel(
      plotOutput("plot")
    )
  )
)
server <- function(input, output){
  v <- reactiveValues(doPlot = FALSE)
  observeEvent(input$go, {
    # 0 will be coerced to FALSE
    # 1+ will be coerced to TRUE
    v$doPlot <- input$go
  })
  observeEvent(input$tabset, {
    v$doPlot <- FALSE
  })  
  output$plot <- renderPlot({
    if (v$doPlot == FALSE) return()
    isolate({
      data <- if (input$tabset == "Uniform") {
        runif(input$unifCount, input$unifRange[1], input$unifRange[2])
      } else {
        rnorm(input$normCount, input$normMean, input$normSd)
      }
      
      hist(data)
    })
  })
}
shinyApp(ui, server)
以上就是大部分會用到button 的情況,今天的內容比較硬一些。
ref:
http://shiny.rstudio.com/articles/action-buttons.html
今天沒有程式碼