[譯]R語言——Shiny框架之入門(一):Shiny應用的基本構成

 

注:以下內容是我個人翻譯自Rstudio官網的Shiny教程,原文地址:http://shiny.rstudio.com/articles/basics.html

水平有限,敬請諒解

在開始之前,先簡單介紹一下Shiny框架,以下內容引用於百度百科:

‘Shiny是R中的一種Web開發框架,使得R的使用者不必太瞭解css、js只需要瞭解一些html的知識就可以快速完成web開發,且shiny包集成了bootstrap、jquery、ajax等特性,極大解放了作爲統計語言的R的生產力。使得非傳統程序員的R使用者不必依賴於前端、後端工程師就可以自己依照業務完成一些簡單的數據可視化工作,快速驗證想法的可靠性。’

Shiny應用的基本構成

Shiny框架自帶十一個例子,本文將使用其中的前三個來展示Shiny應用的基本結構。

例一:Hello Shiny

 

Hello Shiny是一個展示基於R內置的數據集faithful的基本圖形,這個應用包含一個可以調節區間數(bins)的滑動條。要運行這個例子,請輸入代碼:

library(shiny)
runExample("01_hello")

Shiny應用有兩個組成部分,一個用戶界面對象(user interface object)和一個服務功能函數(server function),這兩個部分被作爲參數傳輸到ShinyApp功能函數,再根據這一UI/server對生成一個Shiny應用對象。下面列出了這兩部分的源代碼。

在以後的內容中,我們會將Shiny代碼詳細分解並解釋生成輸出值的“響應式”表示的用法。至於現在,你只需要把玩一下這些簡單的應用然後看一看它們的源代碼,從而獲得一些‘直覺’就好。請一定好好讀讀註釋內容。

用戶界面(UI)部分定義如下:

# Define UI for app that draws a histogram ----
#爲應用(app)定義生成直方圖的UI ----
ui <- fluidPage(

  # App title ----
  # App 標題 ----
  titlePanel("Hello Shiny!"),

  # Sidebar layout with input and output definitions ----
  # 定義包含輸入與輸出的側邊欄佈局 ----
  sidebarLayout(

    # Sidebar panel for inputs ----
    # 輸入的側邊欄面板 ----
    sidebarPanel(

      # Input: Slider for the number of bins ----
      # 輸入:對應直方圖區間數的滑動條 ----
      sliderInput(inputId = "bins",
                  label = "Number of bins:",
                  min = 1,
                  max = 50,
                  value = 30)

    ),

    # Main panel for displaying outputs ----
    # 顯示輸出的主面板 ----
    mainPanel(

      # Output: Histogram ----
      # 輸出:直方圖 ----
      plotOutput(outputId = "distPlot")

    )
  )
)

接下來是應用的服務端(server)。從一個角度來說,它(指服務端)很簡單,其內容僅爲按要求的區間數把隨機分佈用直方圖的形式展示出來。但是你一定注意到了,那部分生成直方圖的代碼被打包在一個叫做renderPlot 的函數當中。這個函數上方的註釋能給出一些解釋,但是如果你還是不明白,不用擔心,我們馬上就會詳細的講解這個概念。

server

# Define server logic required to draw a histogram ----
# 定義生成直方圖所需的server邏輯 ----
server <- function(input, output) {

  # Histogram of the Old Faithful Geyser Data ----
  # with requested number of bins
  # This expression that generates a histogram is wrapped in a call
  # to renderPlot to indicate that:
  #
  # 1. It is "reactive" and therefore should be automatically
  #    re-executed when inputs (input$bins) change
  # 2. Its output type is a plot
------------------------------------------------------------------
  # 根據Old Faithful Geyser(老實泉)數據生成的直方圖 ----
  # 區間數爲用戶定義 ----
  # 這個生成直方圖的語句被封裝在一個叫做renderPlot的函數中
  # 它說明了:
  #
  # 1. 它是“響應式”的,因而應該隨着inputs (input$bins)的
  #    改變而自動執行
  # 2. 它輸出的格式是一個圖表

  output$distPlot <- renderPlot({

    x    <- faithful$waiting
    bins <- seq(min(x), max(x), length.out = input$bins + 1)

    hist(x, breaks = bins, col = "#75AADB", border = "white",
         xlab = "Waiting time to next eruption (in mins)",
         main = "Histogram of waiting times")

    })

}

ShinyApp

終於,我們可以使用shinyApp函數,通過我們定義的上述一對UI/server生成了一個Shiny應用對象

shinyApp(ui, server)

我們保存下所有的代碼,包括ui object,server function還有對shinyApp函數的調用,在一個R語言腳本中被命名爲app.R.

這種結構是所有Shiny應用的基本結構。

接下來的例子會展示更多關於輸入控制和用於生成文本對象的響應式函數的使用方法。

 

例二:Shiny Text

這個Shiny Text應用直接輸出了R中的對象,並以HTML表格的方式顯示了數據框(data frame)。要運行這個例子,請輸入:

library(shiny)
runExample("02_text")

在第一個例子裏,有一個以滑動條方式控制的數字格式的輸入,還有一個單一的圖像輸出。這個例子的內容更多了一些:有兩個輸入值和兩種類型的文本輸出。

如果你把行數(the number of observations)改成別的數字,你就會看到Shiny應用的一個很重要的特性:輸入與輸出的連接是“實時”的,其中的改變會迅速“擴散”(像電子表格一樣)。在本例中,當輸入變化的時候,刷新的僅僅是表格視圖而不是整個頁面。

下面是該應用的UI Object。需要特別注意的是,sidebarPanelmainPanel函數現在調用兩個參數(對應兩個輸入值和兩個輸出):

UI

# Define UI for dataset viewer app ----
# 定義訪問數據集app的UI ----
ui <- fluidPage(

  # App title ----
  # App 標題 ----
  titlePanel("Shiny Text"),

  # Sidebar layout with a input and output definitions ----
  # 定義側邊欄佈局,包含一個輸入和輸出 ----
  sidebarLayout(

    # Sidebar panel for inputs ----
    # 輸入的側邊欄面板 ----
    sidebarPanel(

      # Input: Selector for choosing dataset ----
      # 輸入:下拉菜單選擇數據集 ----
      selectInput(inputId = "dataset",
                  label = "Choose a dataset:",
                  choices = c("rock", "pressure", "cars")),

      # Input: Numeric entry for number of obs to view ----
      # 輸入:輸入數字作爲表格行數 ----
      numericInput(inputId = "obs",
                   label = "Number of observations to view:",
                   value = 10)
    ),

    # Main panel for displaying outputs ----
    # 顯示輸出的主面板 ----
    mainPanel(

      # Output: Verbatim text for data summary ----
      # 輸出:概括性統計量文本 ----
      verbatimTextOutput("summary"),

      # Output: HTML table with requested number of observations ----
      # 輸出:根據輸入行數生成的HTML表格 ----
      tableOutput("view")

    )
  )
)

這個應用的server端同樣變得複雜了一些。現在我們新建:

  • 一個根據用戶選擇返回對應數據集的響應式語句
  • 兩個其他rendering語句(renderPrintrenderTable)返回值爲output$summaryoutput$view

這些語句與第一個例子中的renderPlot語句的作用方法很類似:在聲明一個rendering語句的時候,你告訴Shiny這個語句應該只在其依賴(附屬)變化的時候被執行。在本例中,依賴爲用戶輸入值得其中一個(input$dataset或者input$obs

server

# Define server logic to summarize and view selected dataset ----
# 定義選擇數據集並獲得概括統計量的server邏輯 ----
server <- function(input, output) {

  # Return the requested dataset ----
  # 返回指定的數據集 ----
  datasetInput <- reactive({
    switch(input$dataset,
           "rock" = rock,
           "pressure" = pressure,
           "cars" = cars)
  })

  # Generate a summary of the dataset ----
  # 生成該數據集的概括統計量 ----
  output$summary <- renderPrint({
    dataset <- datasetInput()
    summary(dataset)
  })

  # Show the first "n" observations ----
  # 顯示前n行 ----
  output$view <- renderTable({
    head(datasetInput(), n = input$obs)
  })

}

我們展示了更多的響應式語句但並沒有真正解釋它們是如何運作的。下一個例子會以本例爲基礎,並在此之上極大地擴展Shiny中響應式語句的應用。

例三:Reactivity

這個Reactivity應用與Hello Text應用很類似,但是在響應式編程概念上更進一步。要運行這個例子,請輸入:

library(shiny)
runExample("03_reactivity")

之前的例子讓你看到了一個Shiny應用的基本樣式。我們也多多少少介紹了一些響應式的知識,但是基本上沒有涉及細節上的內容。在這一部分,我們會在這個概念上更加深入,如果你想要徹底地瞭解和學習響應式的細節,請參照“理解響應式”這一部分,其入門:響應式總覽

什麼是響應式

從根本上說,Shiny web框架旨在讓“從web頁面獲取輸入,使其能被R輕鬆獲取和操作,最後又輸出在web頁面上”這件事變得簡單。

input values => R code => output values

因爲Shiny web應用是交互式的,輸入值隨時可能改變,而且輸出值需要根據輸入值的變化即時變化。

Shiny自帶一個響應式編程的library,你可以用它來構建你的應用邏輯。通過使用這個library,input的改變會很自然地使你的R代碼中相關的部分重新執行,從而更新變化後的輸出。

響應式編程基礎

響應式編程是一種代碼風格,這種風格以響應值開始(響應值根據用戶的操作或隨時間進行變化)並在此之上以響應式語句(響應式語句可以獲得響應值並能夠執行其他響應式語句)的形式實現。

關於響應式語句有趣的一點是:無論何時,當它們被執行的時候,都會自動地與響應值和它們引用的響應式語句保持聯繫。如果它們發現它們的“附屬”(“dependencies”)過時了(out of date),那他們也就同樣知道自己的返回值也過時了。由於有這樣的“附屬”追蹤機制,當改變一個響應值的時候,所有直接或間接與之相聯繫的響應式表達式都會被重新執行。

在Shiny中,面對一個響應值的最常見解決辦法是使用input對象。input對象能夠讓你用一個list-like語法獲取web頁面上用戶的輸入信息並且傳輸到你的shinyServer函數。從代碼的角度來說,這個操作看起來就像是你從一個list或者數據框(dataframe)中抓取一個數據一樣,但實際上是你讀取了一個響應值。所以在Shiny裏,我們不需要爲了監視輸入值的變化單獨寫一些代碼,只要寫好讀取輸入值的響應式語句,然後讓Shiny來操心什麼時候該調取他們就好了。

要寫一條響應式語句很簡單:你只需要把一個普通的語句(當然是R語句了!)放到reactive中就好。在這個應用裏的一個例子是:響應式語句根據用戶選擇的數據集返回對應的R數據框(dataframe):

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

爲了能從響應值得到放在web頁面上的輸出結果,我們要把這些響應值對應到output對象上(同樣被傳輸到shinyServer函數)。下面的例子展示瞭如何將兩個輸入(datasetInput和input$obs)的響應式表達式指定到一個輸出上:

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

無論什麼時候,只要datasetInput和input$obs之一發生了變化,表達式就會被重新執行一遍(它所對應的輸出值也會被重新提交給瀏覽器窗口)。

回到代碼上來

我們剛剛對“響應式”這個核心概念有了更深入一些的瞭解,現在咱們重新來看看Reactivity這個例子,試着理解的更徹底一些。UI對象多出來了一個定義“標題”的文本輸入框。除此之外,和前一例非常像:

UI

# Define UI for dataset viewer app ----
# 定義訪問數據集app的UI ----
ui <- fluidPage(

  # App title ----
  # App 標題 ----
  titlePanel("Reactivity"),

  # Sidebar layout with input and output definitions ----
  # 定義側邊欄佈局,包含一個輸入和輸出 ----
  sidebarLayout(

    # Sidebar panel for inputs ----
    # 輸入的側邊欄面板 ----
    sidebarPanel(

      # Input: Text for providing a caption ----
      # Note: Changes made to the caption in the textInput control
      # are updated in the output area immediately as you type
-----------------------------------------------------------------
      # 輸入:定義一個標題的文本 ----
      # 註釋:輸入框裏的文本發生的改變會立刻反應在輸出區域中
      textInput(inputId = "caption",
                label = "Caption:",
                value = "Data Summary"),

      # Input: Selector for choosing dataset ----
      # 輸入:下拉菜單選擇數據集 ----
      selectInput(inputId = "dataset",
                  label = "Choose a dataset:",
                  choices = c("rock", "pressure", "cars")),

      # Input: Numeric entry for number of obs to view ----
      # 輸入:輸入數字作爲表格行數 ----
      numericInput(inputId = "obs",
                   label = "Number of observations to view:",
                   value = 10)

    ),

    # Main panel for displaying outputs ----
    # 顯示輸出的主面板 ----

    mainPanel(

      # Output: Formatted text for caption ----
      # 輸出:對應標題的格式化文本
      h3(textOutput("caption", container = span)),

      # Output: Verbatim text for data summary ----
      # 輸出:概括性統計量文本 ----
      verbatimTextOutput("summary"),

      # Output: HTML table with requested number of observations ----
      # 輸出:根據輸入行數生成的HTML表格 ----
      tableOutput("view")

    )
  )
)

server

server函數聲明瞭datasetInput的響應式表達式還有三個響應式輸出值。註釋中詳細描述了各個定義的細節並解釋了它們在響應式系統中是如何工作的:

# Define server logic to summarize and view selected dataset ----
# 定義可以根據用戶選擇的數據集來獲取概括性統計量和表格的server邏輯 ----
server <- function(input, output) {

  # Return the requested dataset ----
  # By declaring datasetInput as a reactive expression 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,
  #    i.e. it only executes a single time
---------------------------------------------------------------------
  # 返回用戶選擇的數據集 ----
  # 將datasetInput聲明成一個響應式表達式,這樣我們可以保證:
  #
  # 1. 只有當輸入發生改變的時候它纔會被調用
  # 2. 這個表達式的計算結果可以被隨意調用,也就是說:它只執行一遍。
  datasetInput <- reactive({
    switch(input$dataset,
           "rock" = rock,
           "pressure" = pressure,
           "cars" = cars)
  })

  # Create caption ----
  # The output$caption is computed based on a reactive expression
  # that returns input$caption. When the user changes the
  # "caption" field:
  #
  # 1. This function is automatically called to recompute the output
  # 2. New caption is pushed back to the browser for re-display
  #
  # Note that because the data-oriented reactive expressions
  # below don't depend on input$caption, those expressions are
  # NOT called when input$caption changes
------------------------------------------------------------------------
  # 創建標題 ----
  # output$caption是通過一個能夠返回input$caption的響應式語句被計算出來的。
  # 當用戶改變“標題(caption)”區域值的時候:
  # 
  # 1. 這個函數會自動的被調用來重新計算結果
  # 2. 新的結果會被推送回頁面重新顯示
  #
  # 注意:由於下面的一些響應式表達式是面向數據的,他們和input$caption沒什麼關係,也就不會在
  # input$caption發生改變的時候被調用。

  output$caption <- renderText({
    input$caption
  })

  # Generate a summary of the dataset ----
  # The output$summary depends on the datasetInput reactive
  # expression, so will be re-executed whenever datasetInput is
  # invalidated, i.e. whenever the input$dataset changes
-----------------------------------------------------------------------
  # 生成該數據集的概括統計量 ----
  # 由於output$summary依附於datasetInput的響應式語句,所以當datasetInput(原來的值)
  # 無效的時候就會被重新執行一遍,也就是說:無論何時,只要datasetInput發生了改變,
  # 該語句就會重新執行一遍。
  output$summary <- renderPrint({
    dataset <- datasetInput()
    summary(dataset)
  })

  # Show the first "n" observations ----
  # The output$view depends on both the databaseInput reactive
  # expression and input$obs, so it will be re-executed whenever
  # input$dataset or input$obs is changed
--------------------------------------------------------------------------------
  # 顯示前n行 ----
  # output$view同時附屬於datasetInput響應式語句和input$obs變量,
  # 所以當其中一個發生變化的時候,input$obs就會跟着改變。
  output$view <- renderTable({
    head(datasetInput(), n = input$obs)
  })

}

在前三個例子裏,我們看了很多代碼,講解了很多概念性的背景知識。下一篇文章我們將關注如何從零開始建立起一個Shiny應用。

If you have questions about this article or would like to discuss ideas presented here, please post on RStudio Community. Our developers monitor these forums and answer questions periodically. See help for more help with all things Shiny.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章