Learning Shiny

强烈推荐这篇文章http://deanattali.com/blog/building-shiny-apps-tutorial/#15-awesome-add-on-packages-to-shiny

我按照上述教程做了下个人的总结,主要是一些最基础的东西,可能不太全,只是对上述教程的一个总结。

基础篇

在Rstudio上,可以先搭建一个最简单的shiny框架:

library(shiny)
ui <- fluidPage()
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

ui表示shiny的界面,server则表示执行的程序app。

我们除了可以将ui和server简单的写在一个app.R中(如上面所示),还可以将两者分别写入两个文件ui.R、server.R中。

R将html的标签都封装到tags中,可以通过?tags查看h1()、strong()、em()等用法,因此有类似的用法div("this is blue", style = "color: blue;")

UI部分

  1. 添加title

    fluidPage(
        titlePanel("BC Liquor Store prices")
    )
    
  2. 添加布局(简单的布局)

    ui <- fluidPage(
        titlePanel("BC Liquor Store prices"),
        sidebarLayout(
            sidebarPanel("our inputs will go here"),
            mainPanel("the results will go here")
        )
    )
    

    注意每个函数后面需要逗号进行隔开

  3. 标准shiny部件

    actionButton -> 执行按钮
    submitButton -> 提交按钮
    checkboxGroupInput -> 一组复选框
    checkInput -> 一个单一复选框
    dataInput -> 一个日期选择器
    dataRangeInput -> 时间范围选择器
    fileInput 上传文件
    helpText -> 帮助文档
    numericInput -> 输入数值
    radioButtons -> 单选按钮
    selectInput -> 可选择的待选盒
    sliderInput -> 滑动条
    textInput -> 输入文本
    

    如:

    checkboxInput("checkbox", label="Choice A", value = TRUE)
    

    checkbox为inputID, Choice A为部件名称, value为的默认值,如果点了这个按钮则表示输入“TRUE”

    sliderInput("priceInput", "Price", min = 0, max = 100,
                         value = c(25, 40), pre = "$")
    )  
    

    min为sliderInput所要设定的最小值,max则为最大值,pre为在值前面添加的符号,在条形框中可以在范围内任意选择两个值,一个最大值,一个最小值

    radioButtons("typeInput", "Product type",
        choices = c("BEER", "REFRESHMENT", "SPIRITS", "WINE"),
        selected = "WINE")  
    

    choices为选择的按钮值,selected为传输的默认值,选择choices中的一个,则表示将这个值输入到server中执行

    selectInput("countryInput", "Country",
        choices = c("CANADA", "FRANCE", "ITALY"))  
    

    出现一个下拉框,可以选择choices中的任意一个作为输入值

    将以上连起来放置在sidebarLayout()就是多个的shiny部件的集合体

  4. 除了输入的展现形式外,在UI界面还有一个输出的展现形式

    类似于inputId,outputId也需要唯一。函数需要可以写在mainPanel()中,也就是界面的右半边

  5. 添加图片在UI界面

    img(src = 'image.png')
    

    注:图片一般是放在与ui.R以及server.R同目录下的www文件夹内。如果是RStudio中执行的话,可以将图片放在R目录下library\shiny\www内(具体路径因人而异)

Server部分

Server函数主要负责将inputs转变为outputs展现在shiny app上,因此必须有input and output。input就是通过部件读入的values,output则是你将input转化并展现的values。

  1. 构建一个output

    首先需要在UI中有个输出的定义,如:

    mainPanel(plotOutput("coolplot"))
    
  2. 然后在server中将部件中的priceInput的value输入,并以plot图展示在”coolplot”中,因为在UI中使用的是plotOutput,所以在server中要使用对应的renderPlot,并将plot放置在renderPlot函数中。

    output$coolplot <- renderPlot({
        plot(rnorm(input$priceInput[1]))
    })
    

    每个输出都要有个唯一的outputID,这里就是指”coolplot”

  3. 我们也可以在shiny app中调用其他包,比如ggplot来作图

    output$coolplot <- renderPlot({
        ggplot(bcl, aes(Alcohol_Content)) +
         geom_histogram()
    })
    ###bcl是读入的数据框###
    
  4. 除了plot还有常用的table output

    output$results <- renderTable({})
    

Reactivity部分

Shiny app 能够根据input数据即时改变output数据,这是依靠reactive程序来实现的

  1. 除了render*函数,还有另外两个常用的的函数reactive({})observe({})

    observe(print(input$priceInput))  
    

    他会输出priceInput值,并且随着部件中priceInput值的变化而变化,输出在console上。如果不加observe,直接print会报错。这种输出方式也是很好的一种排错的方法之一

  2. observe({})reactive({})的区别在于后者可以返回value,前者在于展现value(在console),如:

    priceDiff <- reactive({
        diff(input$priceInput)
    })
    observe({ print(priceDiff()) })
    ###注意这里用的是priceDiff(),而不是priceDiff
    

    因此我们可以将一些R运算放置在reactive中,以减少一些重复的代码。比如一些既在renderPlot({})出现也在renderTable({})出现的运算,可以通过reactive({})来整合成一个reactive变量,然后在后续render*中调用即可

使用uiOutput()创建动态UI元素

  1. 一般来说,我们在UI创建的input在shiny app运行前就固定了,但如果你想通过一个input来改变另外一个input,则可以使用uiOutput()来创建动态的input,uiOutput()与之对应在server的是renderUI

    ui <- fluidPage(
        numericInput("num", "Maximum slider value", 5),
        uiOutput("slider")
    )
    
    server <- function(input, output) {
        output$slider <- renderUI({
            sliderInput("slider", "Slider", min = 0,
                max = input$num, value = 0)
        })  
    }
    
    shinyApp(ui = ui, server = server)
    

    可以看到,在ui中,已用uiOutput()代替之前原来的sliderInput(),然后在renderUI()设置了一个新的sliderInput(),其值是随着numericInput()的改变而改变,这是一种很常用的表现形式。

  2. 使用uiOutput时还要注意一点其运行流程的顺序。比如,如果在server中使用renderUI,然后在其后就要调用renderUI所产生的value,这样是error的。因为renderUI产生的value还需返回到UI中才能被server里的后续函数调用,所以我们需要自行增加判断语句来识别,以过滤掉这error,尽管这个error不会对整个app的运行造成阻碍。

  3. 如果我们已经固定了某个UI Input,然后希望在后续又能对其进行修改,则可以使用Input所对应的update*input来修改上述参数,如:

    ui <- fluidPage(
        sliderInput("slider", "Move me", value = 5, 1, 10),
        numericInput("num", "Number", value = 5, 1, 10)
    )
    server <- function(input, output, session) {
        observe({
            updateNumericInput(session, "num", value = input$slider)
        })
    }
    shinyApp(ui = ui, server = server)
    

    通过上述代码,我们即可在UI部件numicInput中手动修改参数,也可以通过修改sliderInput中的value来更新numericInput中的value