大家可能看了標題會有些不懂,這邊我所指的分段處理意思是,有時候你的原始資料並不是很完善,要先做流程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
今天沒有程式碼