[譯]R語言——Shiny框架之入門(二):如何構建一個Shiny應用

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

水平有限,敬請諒解

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

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

如何構建一個Shiny應用

我們先總覽一下構建一個簡單Shiny應用的步驟。一個Shiny應用其實只是一個包含一個R語言腳本的目錄,這個腳本文件叫做app.R,它的內容包括UI對象和server函數。這個文件夾(目錄)當然也可以放很多其他東西,比如任何與項目有關的數據,腳本文件,或者一些你需要的其他東西。

UI & Server

要開始構建Shiny應用,首先你得創建一個新的空白目錄,放在哪無所謂。然後在這個目錄下創建一個空白的app.R文件。爲了講解起來方便,咱們就假設你在~/shinyapp:目錄下創建了這個應用。

~/shinyapp
|-- app.R

現在,我們在這個app.R文件裏寫下構造一個應用所需的很少的代碼。

首先,我們加載shiny包:

library(shiny)

ui

然後,我們調用一個叫做pageWithSidebar的函數來定義ui界面:

# Define UI for miles per gallon app ----
# 爲mpg(油耗) app定義UI ----
ui <- pageWithSidebar(

  # App title ----
  # App 標題 ----
  headerPanel("Miles Per Gallon"),

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

  # Main panel for displaying outputs ----
  # 帶輸出的主面板 ----
  mainPanel()
)

上面的三個函數:headerPanel,sidebarPanel和mainPanel定義了UI界面的不同區域。這個應用叫做“油耗(英里每加侖)”所以我們在定義標題面板的時候直接把名字寫上去了,而其他兩個函數目前還是空的。

現在,我們來定義一個server的執行結構。爲此,我們調用一個shinyServer然後給它一個可以接受input和output兩個變量的函數(function):

server

# Define server logic to plot various variables against mpg ----
# 定義可以展示多個mpg變量的server邏輯
server <- function(input, output) {

}

我們的server函數目前還是空的,但是等一下我們就會定義input和output之間的聯繫了。

app.R

終於,我們可以在shinyApp函數裏用剛剛定義的ui object和server function來構建一個Shiny應用對象了!

把它倆放到一起去,我們的app.R腳本文件就變成了這樣:

library(shiny)

# Define UI for miles per gallon app ----
ui <- pageWithSidebar(
  
  # App title ----
  headerPanel("Miles Per Gallon"),
  
  # Sidebar panel for inputs ----
  sidebarPanel(),
  
  # Main panel for displaying outputs ----
  mainPanel()
)

# Define server logic to plot various variables against mpg ----
server <- function(input, output) {
  
}

shinyApp(ui, server)

現在我們創建了一個可以執行的最小的Shiny應用,你可以調用runApp函數來執行這個程序:

> library(shiny)
> runApp("~/shinyapp")

或者,如果你還需要使用控制檯,你也可以點擊RStudio界面上的Run App按鈕。

如果沒有問題的話,你會在窗口看到:

現在我們獲得了一個可以執行的Shiny應用,儘管它沒什麼功能。接下來的部分我們將要通過增添ui特性和server函數來完成這個應用。

輸入和輸出

將輸入值添加到側邊欄中

我們即將創建的這個應用將使用到R自帶數據集中的mtcars數據,用戶可以在一個箱線圖中觀察到英里每加侖(MPG)變量和其他三個變量(氣缸數(Cylinders),變速箱形式(Transmission),檔位數(gears))間的關係。

我們想要提供一種能夠選擇MPG對應變量的辦法,再加一個選擇是否顯示極值的選項。爲了實現這個想法,我們在側邊欄中添加兩個元素:一個selectInput用來指定某個變量,還有一個checkboxInput用來控制極值是否顯示。在添加了這兩個元素之後,我們的ui代碼變成了這樣:

ui

# Define UI for miles per gallon app ----
# 爲mpg(油耗) app定義UI ----
ui <- pageWithSidebar(
  
  # App title ----
  # App標題 ----
  headerPanel("Miles Per Gallon"),
  
  # Sidebar panel for inputs ----
  # 帶輸入的側邊欄面板 ----
  sidebarPanel(
  
        # Input: Selector for variable to plot against mpg ----
        # 輸入:與mpg對應的變量選擇器 ----
      selectInput("variable", "Variable:", 
                c("Cylinders" = "cyl",
                  "Transmission" = "am",
                  "Gears" = "gear")),

      # Input: Checkbox for whether outliers should be included ----
      # 輸入:關於極值是否顯示的選項 ----
      checkboxInput("outliers", "Show outliers", TRUE)
  
  ),
  
  # Main panel for displaying outputs ----
  # 顯示輸出的主面板 ----
  mainPanel()
)

如果你在更改完代碼之後再次運行這個應用,你就會看到在側邊欄中出現了兩個輸入的元素:

創建server函數

接下來我們需要定義應用的server端,server端會接收inputs並且運算輸出outputs。下面就是我們的server函數,並且列出了幾條重要的概念:

  • 在input對象上獲取輸入信息並通過將其與output對象連接來生成輸出信息
  • 在應用啓動時獲得的數據將能夠被應用在它運行的時候一直使用
  • 用響應式語句計算出的值將能夠被多個輸出變量調用

Shiny的server函數的一個基本作用是定義連接輸入與輸出之間的某種關係。我們的函數通過對獲取的輸入信息進行一系列的計算並且將反應式語句指定到輸出值來實現這一功能。

下面是完整的server函數代碼(其中的註釋解釋了代碼具體實施時的細節):

server

# Data pre-processing ----
# Tweak the "am" variable to have nicer factor labels -- since this
# doesn't rely on any user inputs, we can do this once at startup
# and then use the value throughout the lifetime of the app
----------------------------------------------------------------------
# 數據預處理 ----
# 將"am"變量轉換成擁有更好標籤的因子變量 -- 由於這個變量不依賴於任何輸入,
# 我們可以在一開始就對它進行處理,這樣就可以在整個app中使用處理後的變量了
mpgData <- mtcars
mpgData$am <- factor(mpgData$am, labels = c("Automatic", "Manual"))

# Define server logic to plot various variables against mpg ----
# 定義用來展示與mpg相關聯的多個變量的server邏輯 ----
server <- function(input, output) {

  # Compute the formula text ----
  # This is in a reactive expression since it is shared by the
  # output$caption and output$mpgPlot functions
------------------------------------------------------------------
  # 生成格式化字符 ----
  # 因爲這個格式化字符變量會被output$caption和output$mpgPlot的函數調用,
  # 所以把它放在一個reactive中
  formulaText <- reactive({
    paste("mpg ~", input$variable)
  })

  # Return the formula text for printing as a caption ----
  # 將格式化文本賦值給標題變量 ----
  output$caption <- renderText({
    formulaText()
  })

  # Generate a plot of the requested variable against mpg ----
  # and only exclude outliers if requested
-------------------------------------------------------------------
  # 根據用戶所選變量生成一個與mpg相關聯的圖表 ----
  # 並且僅當選項被勾選時顯示極值
  output$mpgPlot <- renderPlot({
    boxplot(as.formula(formulaText()),
            data = mpgData,
            outline = input$outliers,
            col = "#75AADB", pch = 19)
  })

}

使整個應用成爲“響應式”的原因是使用了renderText和renderPlot函數來生成輸出變量(而非簡單的直接指定輸出變量)。這些響應式的封裝返回了一些特別的語句,這些語句只會在他們的附屬發生變化的時候重新執行。正是這樣的功能(行爲)使得Shiny可以根據輸入的變化自動更新輸出。

顯示輸出

server函數爲兩個輸出變量賦值:output$caption和output$mpgPlot

爲了能夠在我們的ui界面顯示這些輸出變量,我們需要在主ui面板上添加一些元素。

在下面的代碼中,你可以看到我們爲更新後的ui定義添加了關於caption的h3元素,用textOutput函數對其賦值,並調用plotOutput函數表達出所需圖形:

ui

# Define UI for miles per gallon app ----
ui <- fluidPage(

  # App title ----
  titlePanel("Miles Per Gallon"),

  # Sidebar layout with input and output definitions ----
  sidebarLayout(

    # Sidebar panel for inputs ----
    sidebarPanel(

      # Input: Selector for variable to plot against mpg ----
      selectInput("variable", "Variable:",
                  c("Cylinders" = "cyl",
                    "Transmission" = "am",
                    "Gears" = "gear")),

      # Input: Checkbox for whether outliers should be included ----
      checkboxInput("outliers", "Show outliers", TRUE)

    ),

    # Main panel for displaying outputs ----
    mainPanel(

      # Output: Formatted text for caption ----
      h3(textOutput("caption")),

      # Output: Plot of the requested variable against mpg ----
      plotOutput("mpgPlot")

    )
  )
)

現在運行這個應用,就能在頁面上看到包含輸入和動態輸出的最終形態:

一些細節

shinyApp()函數返回了一個shiny.appobj類的對象。當這個對象被返回到控制檯的時候,它會被用print.shiny.appobj()函數輸出,而這個函數會啓動一個關聯前述對象的Shiny應用。

你也可以用類似的技術方法來創建一個不叫app.R而且不在他們自己目錄下的文件。舉個例子,假如你創建一個叫做test.R的文件然後用shinyApp()來調用它,你可以在控制檯裏這樣做:

print(source("test.R"))

當對一個文件使用source()方法之後,它會返回一個shiny.appobj對象,但在默認情況下,source()方法的返回值是不會被顯示出來的。把它們放在print()中能夠讓Shiny框架啓動並顯示這個文件。

以上的辦法對於開始一個小實驗來說很方便,但是與在文件自己的目錄下的app.R相比,這個辦法還是要差一些。當你使用runApp("newdir")的時候,Shiny會監控這個文件的變動並且會在你重載瀏覽器的同時重載這個應用,這個功能對於開發工作是很有用的。但是如果你只是對文件調用source()的話,Shiny就不會這麼做了。同樣地,Shiny Server和shinyapps.io需要應用在它自己的目錄下。所以如果你想要使用你的app,你需要讓它待在自己的目錄下。

現在,我們有了一個簡單的應用,但是我們還想要再做一些改變。下一篇文章的內容會包含編輯,運行和調試Shiny應用的整個流程。

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