Introduction
Shiny 是一個 R 包,可輕松地直接從 R 構建交互式 Web 應用程序。還可以在網頁上托管獨立應用程序或將它們嵌入 R Markdown 文檔或構建儀表板,還可以使用 CSS 主題、html 小部件和 JavaScript 操作擴展您的 Shiny 應用程序。
#first app
library(shiny)
ui <- fluidPage(
"Hello, world!"
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
整個app可分為兩個部分,ui與sever,可以認為是前端與后端的關系,Rshiny提供了很多內置的小組件幫我們將前后端聯(lián)系起來,非常方便好用。只需要有一點點的HTML/CSS/Javascript的知識就可以設計出更好看的界面和更多有趣的交互。
這里是shiny的記憶手冊,濃縮了大部分的操作。
Methods
UI設計
Layout
最基礎的布局就是sidebarLayout
,可以分成sidebarPanel
,mainPanel
兩個部分。
ui <- fluidPage(
titlePanel("title panel"),
sidebarLayout(
sidebarPanel("sidebar panel"),
mainPanel("main panel")
)
)
可以通過將內容放在 *Panel 函數中來向Shiny 應用程序添加內容,shiny提供了R代碼方便插入HTML (Table <a href="#tab:html">1</a>),更多標簽可以查看tag-glossary。
Control widgets
這些是常用shiny內置的小控件,提供一種向 Shiny serve發(fā)送消息的方式。
可以想想怎么獲取更多控件,比如一個color panel等等。
更多:
使用 textInput() 收集少量文本,使用 passwordInput()3 收集密碼,使用 textAreaInput() 收集文本段落。
要收集數值,請使用 numericInput() 創(chuàng)建一個受約束的文本框或使用 sliderInput() 創(chuàng)建一個滑塊。如果您為 sliderInput() 的默認值提供一個長度為 2 的數值向量,您將得到一個具有兩端的“范圍”滑塊。
使用 dateInput() 收集一天或使用 dateRangeInput() 收集兩天的范圍。這些提供了一個方便的日歷選擇器,并且諸如 datesdisabled 和 daysofweekdisabled 之類的附加參數允許您限制有效輸入的集合。
有兩種不同的方法允許用戶從一組預先指定的選項中進行選擇:selectInput()(還可以設置 multiple = TRUE 以允許用戶選擇多個元素)和 radioButtons(); 可以使用checkboxGroupInput()形成多選。
ui <- fluidPage(
textInput("name", "What's your name?"),
passwordInput("password", "What's your password?"),
textAreaInput("story", "Tell me about yourself", rows = 3)
)
ui <- fluidPage(
numericInput("num", "Number one", value = 0, min = 0, max = 100),
sliderInput("num2", "Number two", value = 50, min = 0, max = 100),
sliderInput("rng", "Range", value = c(10, 20), min = 0, max = 100)
)
ui <- fluidPage(
dateInput("dob", "When were you born?"),
dateRangeInput("holiday", "When do you want to go on vacation next?")
)
animals <- c("dog", "cat", "mouse", "bird", "other", "I hate animals")
ui <- fluidPage(
selectInput("state", "What's your favourite state?", state.name),
radioButtons("animal", "What's your favourite animal?", animals),
checkboxGroupInput("animal", "What animals do you like?", animals)
)
讓用戶使用 actionButton() 或 actionLink() 執(zhí)行操作,可以使用“btn-primary”、“btn-success”、“btn-info”、“btn-warning”或“btn-danger”之一使用類參數自定義外觀,使用“btn-lg”、“btn-sm”、“btn-xs”更改大小,可以使用“btn-block”使按鈕跨越它們嵌入的元素的整個寬度。
ui <- fluidPage(
fluidRow(
actionButton("click", "Click me!", class = "btn-danger"),
actionButton("drink", "Drink me!", class = "btn-lg btn-success")
),
fluidRow(
actionButton("eat", "Eat me!", class = "btn-block")
)
)
連接sever
general
- 添加輸出對象在ui中
Shiny 提供了一系列函數,可以將 R 對象轉換為用戶界面的輸出。每個函數創(chuàng)建特定類型的輸出。
-
編寫構建R對象的代碼在serve中
- 使用input,output連接
以下代碼就可以將selectInput選擇的值var
通過input$var
的形式傳遞到renderText()
,再render成output$selected_var
的形式,最后通過textOutput()
輸出到ui界面。
library(shiny)
ui <- fluidPage(
titlePanel("censusVis"),
sidebarLayout(
sidebarPanel(
selectInput("var",
label = "Choose a variable to display",
choices = c("Percent White",
"Percent Black",
"Percent Hispanic",
"Percent Asian"),
selected = "Percent White")
),
mainPanel(
textOutput("selected_var")
)
)
)
server <- function(input, output) {
output$selected_var <- renderText({
paste("You have selected", input$var)
})
}
shinyApp(ui, server)
更多:
使用 textOutput() 輸出常規(guī)文本,使用 verbatimTextOutput() 輸出固定代碼和控制臺輸出。
renderText() 將結果組合成一個字符串,通常與 textOutput() 配對
renderPrint() 打印結果,就像您在 R 控制臺中一樣,并且通常與 verbatimTextOutput() 配對。
tableOutput() 和 renderTable() 呈現(xiàn)靜態(tài)數據表,同時顯示所有數據。
dataTableOutput() 和 renderDataTable() 呈現(xiàn)一個動態(tài)表,顯示固定數量的行以及用于更改哪些行可見的控件。
tableOutput() 對于小型、固定的摘要(例如模型系數)最有用;如果您想向用戶公開完整的數據框,則 dataTableOutput() 是最合適的。
默認情況下,plotOutput() 將占據其容器的整個寬度(稍后會詳細介紹),并且高度為 400 像素。您可以使用高度和寬度參數覆蓋這些默認值。我們建議始終設置 res = 96,因為這將使您的 Shiny 圖與您在 RStudio 中看到的盡可能接近。
reactive expressions
This difference between commands and recipes is one of the key differences between two important styles of programming:
In imperative programming, you issue a specific command and it’s carried out immediately. This is the style of programming you’re used to in your analysis scripts: you command R to load your data, transform it, visualise it, and save the results to disk.
In declarative programming, you express higher-level goals or describe important constraints, and rely on someone else to decide how and/or when to translate that into action. This is the style of programming you use in Shiny.
Shiny 中聲明式編程的優(yōu)勢之一是它允許應用程序非常懶惰。 Shiny 應用程序只會執(zhí)行更新您當前可以看到的輸出控件所需的最少工作量,優(yōu)點是快速,缺點是如果你不運行所有代碼,可能不會發(fā)現(xiàn)錯誤。
反應式表達式比常規(guī) R 函數更聰明。它們緩存值并知道它們的值何時會變。第一次運行反應式表達式時,表達式會將其結果保存在計算機的內存中。下次調用反應式表達式時,它可以返回保存的結果而不進行任何計算(這將使您的應用程序更快)。
如果反應式表達式知道結果是最新的,它只會返回保存的結果。如果反應式表達式得知結果已過時(因為小部件已更改),則表達式將重新計算結果。然后它返回新結果并保存一個新副本。反應式表達式將使用這個新副本,直到它也變得過時為止。
讓我們總結一下這種行為:
反應式表達式會在您第一次運行時保存其結果。
下次調用反應式表達式時,它會檢查保存的值是否已過時(即,它所依賴的小部件是否已更改)。
如果該值已過期,反應對象將重新計算它(然后保存新結果)。
如果該值是最新的,反應式表達式將返回保存的值而不進行任何計算。
建議把文件導入等不需要每次更新的代碼放在reactive里。
在 Shiny 中,應該考慮一個規(guī)則:每當復制和粘貼一次東西時,就應該考慮將重復的代碼提取到一個反應表達式中,因為反應式表達式不僅讓人類更容易理解代碼,它們還提高了 Shiny 高效重新運行代碼的能力。
考慮以下代碼,reactive()
確保只有在改變n或lambda時才會重新計算:
library(ggplot2)
freqpoly <- function(x1, x2, binwidth = 0.1, xlim = c(-3, 3)) {
df <- data.frame(
x = c(x1, x2),
g = c(rep("x1", length(x1)), rep("x2", length(x2)))
)
ggplot(df, aes(x, colour = g)) +
geom_freqpoly(binwidth = binwidth, size = 1) +
coord_cartesian(xlim = xlim)
}
ui <- fluidPage(
fluidRow(
column(3,
numericInput("lambda1", label = "lambda1", value = 3),
numericInput("lambda2", label = "lambda2", value = 5),
numericInput("n", label = "n", value = 1e4, min = 0)
),
column(9, plotOutput("hist"))
)
)
server <- function(input, output, session) {
x1 <- reactive(rpois(input$n, input$lambda1))
x2 <- reactive(rpois(input$n, input$lambda2))
output$hist <- renderPlot({
freqpoly(x1(), x2(), binwidth = 1, xlim = c(0, 40))
}, res = 96)
}
shinyApp(ui,server)
控制reactive行為:
- 計時器
reactiveTimer()
是一個響應式表達式,它依賴于隱藏的輸入:當前時間。
將server里的reactive修改成下列形式可以讓圖形每500ms刷新一次。
server <- function(input, output, session) {
timer <- reactiveTimer(500)
x1 <- reactive({
timer()
rpois(input$n, input$lambda1)
})
x2 <- reactive({
timer()
rpois(input$n, input$lambda2)
})
output$hist <- renderPlot({
freqpoly(x1(), x2(), binwidth = 1, xlim = c(0, 40))
}, res = 96)
}
- 點擊刷新
當你的serve運行一次需要龐大計算和時間時,可能希望要求用戶通過單擊按鈕來選擇執(zhí)行昂貴的計算。這是 actionButton() 的一個很好的用例:
并且我們需要 eventReactive()
,它有兩個參數:第一個參數指定依賴什么,第二個參數指定計算什么。
修改ui和serve,添加了按鍵,用戶點擊按鍵即可出現(xiàn)新的模擬結果。
ui <- fluidPage(
fluidRow(
column(3,
numericInput("lambda1", label = "lambda1", value = 3),
numericInput("lambda2", label = "lambda2", value = 5),
numericInput("n", label = "n", value = 1e4, min = 0),
actionButton("simulate", "Simulate!")
),
column(9, plotOutput("hist"))
)
)
server <- function(input, output, session) {
x1 <- eventReactive(input$simulate, {
rpois(input$n, input$lambda1)
})
x2 <- eventReactive(input$simulate, {
rpois(input$n, input$lambda2)
})
output$hist <- renderPlot({
freqpoly(x1(), x2(), binwidth = 1, xlim = c(0, 40))
}, res = 96)
}
observeEvent() 與 eventReactive() 非常相似。它有兩個重要的參數:eventExpr 和 handlerExpr。第一個參數是要依賴的輸入或表達式;第二個參數是將要運行的代碼。例如,對 server() 的以下修改意味著每次更新該名稱時,都會向控制臺發(fā)送一條消息:
ui <- fluidPage(
textInput("name", "What's your name?"),
textOutput("greeting")
)
server <- function(input, output, session) {
string <- reactive(paste0("Hello ", input$name, "!"))
output$greeting <- renderText(string())
observeEvent(input$name, {
message("Greeting performed")
})
}
file up/download
使用fileInput
在ui中上傳文件后,得到的input是一個列表,其中的datapath是文件上傳后的路徑,需要使用read.csv等函數讀取file$datapath。
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File", accept = ".csv"),
checkboxInput("header", "Header", TRUE)
),
mainPanel(
tableOutput("contents")
)
)
)
server <- function(input, output) {
output$contents <- renderTable({
file <- input$file1
ext <- tools::file_ext(file$datapath)
req(file)
validate(need(ext == "csv", "Please upload a csv file"))
read.csv(file$datapath, header = input$header)
})
}
shinyApp(ui, server)
您可以讓用戶使用 downloadButton() 或 downloadLink() 下載文件。這些都需要服務器功能中的新技術,因此我們將在第 9 章中回過頭來討論。
Share
文件形式
任何擁有 R 的人都可以運行Shiny 應用程序,分享你的app.R 文件副本,以及您的應用程序中使用的任何補充材料(例如,www 文件夾或 helpers.R 文件)即可,最好寫上代碼運行的依賴包安裝代碼。
-
runUrl()
will download and launch a Shiny app straight from a weblink. runGitHub( "<your repository name>", "<your user name>")
網頁形式
上述方法要求用戶在他們的計算機上安裝 R 和 Shiny。但如果我們自己搭建好了服務器,也可以直接用瀏覽器使用我們的APP。
- Shinyapps.io
將 Shiny 應用程序轉換為網頁的最簡單方法是使用 shinyapps.io,這是 RStudio 為 Shiny 應用程序提供的托管服務。
Shiny Server
RStudio Connect
Reference
參考書:Mastering Shiny