Shiny从入门到入定——1.在小小的花园里面挖呀挖呀挖
发表于:2023-11-10 | 分类: IT
字数统计: 3.6k | 阅读时长: 13分钟 | 阅读量:

前言

本部分为学习 Shiny 的第一阶段 —— 挖坑。

在接下来的四章中,目标是让读者尽快学会编写 Shiny 应用程序。

  • 在第一章中,作者将从小处着手,但内容完整,向读者展示应用程序的所有主要部分以及它们如何组合在一起。

  • 在第二章和第三章中,读者将开始详细了解 Shiny 应用程序的两个主要部分:前端(用户在浏览器中看到的内容)和后端(使应用程序起作用的代码)。

  • 第四章将通过案例研究来完成,以帮助巩固读者迄今为止所学的概念。

第一个 Shiny app

简介

在本章中,我们将创建一个简单的 Shiny 应用程序。首先,我将向您展示 Shiny 应用程序所需的最低样板,然后您将学习如何启动和停止它。接下来,您将学习每个 Shiny 应用程序的两个关键组件:UI(用户界面的缩写),它定义了应用程序的外观,以及服务器功能,它定义了应用程序的工作方式。Shiny 使用响应式编程在输入更改时自动更新输出,因此我们将通过学习 Shiny 应用程序的第三个重要组件:响应式表达式来结束本章。

如果你还没有安装 Shiny,现在用以下命令安装它

install.packages("shiny")

如果您已经安装了 Shiny,请使用 packageVersion("shiny") 检查您是否安装了 1.5.0 或更高版本。然后在当前的 R 会话中加载:

library(shiny)

创建应用程序目录和文件

创建 Shiny 应用程序有多种方法,最简单的是为您的应用程序创建一个新目录,并在其中放置一个名为 app.R 的单个文件。这个 app.R 文件将用于告诉 Shiny 应用程序应该如何展示界面以及应该如何运行。

通过创建新目录并添加如下所示的 app.R 文件来尝试一下:

library(shiny)
ui <- fluidPage(
  "Hello, world!"
)
server <- function(input, output, session) {
}
shinyApp(ui, server)

以上是完整的、基础的 Shiny 应用程序!仔细观察上面的代码,app.R 做了四件事:

  • 它调用 library(shiny) 来加载 shiny 包。

  • 它定义了用户界面,即人类与之交互的 HTML 网页。在这种情况下,它是一个包含 “Hello,world!” 的页面。

  • 它通过定义 server 函数来指定应用程序的行为。目前为空,因此我们的应用程序不做任何事情,但我们稍后会回过头来重新审视这一点。

  • 它使用 shinyApp(ui,server) 来构建并启动一个从用户界面和服务器构建的 Shiny 应用程序。

RStudio 提示:在 RStudio 中有两种方便的方法可以创建新的应用程序:

  • 通过点击 File | New Project ,然后选择 New DirectoryShiny Web Application ,可以一次性创建一个新的目录和 app.R 文件,其中包含基本的应用程序。

  • 如果您已经创建了 app.R 文件,可以通过键入 “shinyapp” 并按 Shift+Tab 快速添加应用程序样板。

运行和停止

您可以通过几种方式运行此应用程序:

  • 单击文档工具栏中的 Run App (图 1.1)按钮。

  • 使用键盘快捷键: Cmd / Ctrl + Shift + Enter

  • 如果您没有使用 RStudio,则可以使用 (source()) 1 整个文档,或使用包含 app.R 的目录的路径调用 shiny::runApp()

图1.1 Run App 按钮可以在源码窗格的右上角找到。

从这些选项中选择一个,并确认您看到的与图 1.2 中的相同的应用程序。恭喜!您已经制作了您的第一个 Shiny 应用程序。

图1.2 当你运行上面的代码时,你会看到最简单的shiny应用程序

在你关闭应用程序之前,回到 RStudio 并查看 R 控制台。你会注意到它说类似于:

#> Listening on http://127.0.0.1:3827

这告诉你应用程序的 URL:127.0.0.1 是一个标准地址,表示 “这台计算机”,而 3827 是一个随机分配的端口号。你可以将该 URL 输入到任何兼容的 web 浏览器中以打开应用程序的另一个副本。

另外请注意,R 正在运行:R 提示符不可见,控制台工具栏显示一个停止符号图标。当 Shiny 应用程序正在运行时,它会 “阻止” R 控制台。这意味着在 Shiny 应用程序停止之前,您无法在 R 控制台运行新的命令。

您可以使用以下选项之一停止应用程序并返回对控制台的访问权限:

  • 点击 R 控制台工具栏上的停止符号图标。

  • 单击控制台,然后按 Esc(或按 Ctrl + C 如果您没有使用 RStudio)。

  • 关闭 Shiny 应用程序窗口。

Shiny 应用程序开发的基本工作流程是编写一些代码,启动应用程序,与应用程序交互,编写更多代码,然后重复。如果你使用的是 RStudio,你甚至不需要停止和重新启动应用程序来查看你的更改 - 你可以按下工具箱中的 Reload app 按钮或使用 Cmd/Ctrl + Shift + Enter 键盘快捷键。我将在第 5 章中介绍其他工作流程模式。

添加用户界面控件

接下来,我们将向用户界面添加一些输入和输出,使其不再如此简约。我们将创建一个非常简单的应用程序,向您展示 datasets 包中包含的所有内置数据框。

用以下代码替换您的 ui:

ui <- fluidPage(
  selectInput("dataset", label = "Dataset", choices = ls("package:datasets")),
  verbatimTextOutput("summary"),
  tableOutput("table")
)

这个例子使用了四个新函数:

  • fluidPage() 是一个布局函数,用于设置页面的基本视觉结构。您将在第 6.2 节中了解更多关于它们的信息。

  • selectInput() 是一种输入控件,使用户可以通过提供值与应用程序进行交互。在这种情况下,它是一个带有标签 “Dataset” 的下拉菜单,允许您从 R 自带的内置数据集中选择一个。您将在第 2.2 节中了解更多关于输入的信息。

  • verbatimTextOutput()tableOutput() 是输出控件,它们告诉 Shiny 将渲染的输出放在哪里(我们稍后再了解)。 verbatimTextOutput() 显示代码, tableOutput() 显示表格。您将在第 2.3 节中了解更多关于输出的信息。

布局函数、输入和输出有不同的用途,但它们在本质上都是相同的:它们都是生成 HTML 的巧妙方式,如果您在 Shiny 应用程序之外调用其中任何一个,您将在控制台上看到 HTML 输出。不要害怕深入研究这些不同的布局和控件是如何工作的。

现在运行应用程序。您将看到图 1.3,一个包含下拉框的页面。我们只看到了输入,而没有看到两个输出,因为我们还没有告诉 Shiny 输入和输出之间的关系。

图1.3 具有用户界面的datasets应用程序

添加行为

接下来,我们将通过在 server 函数中定义它们来为输出提供生命。

Shiny 使用响应式编程使应用程序具有交互性。您将在第 3 章中了解更多关于响应式编程的信息,但现在只需知道,它涉及到告诉 Shiny 如何执行计算,而不是命令 Shiny 实际去做。这就像给某人食谱与命令他们给您做三明治之间的区别。

我们将为样本应用程序中的摘要和表格输出提供 “食谱”,以告诉 Shiny 如何填充这些输出。用以下代码替换您的空 server 函数:

server <- function(input, output, session) {
  output$summary <- renderPrint({
    dataset <- get(input$dataset, "package:datasets")
    summary(dataset)
  })
  
  output$table <- renderTable({
    dataset <- get(input$dataset, "package:datasets")
    dataset
  })
}

赋值运算符(<-)的左侧, output$ID ,表示您正在为具有该 ID 的 Shiny 输出提供食谱。赋值运算符的右侧使用特定的 render 函数来包装您提供的代码。每个 render{Type} 函数旨在生成特定类型的输出(例如文本、表格和图形),并且通常与 {type}Output 函数配对。例如,在此应用程序中, renderPrint()verbatimTextOutput() 配对以显示具有固定宽度(逐字)文本的统计摘要,而 renderTable()tableOutput() 配对以在表格中显示输入数据。

再次运行应用程序并尝试使用,观察更改输入时输出会发生什么变化。图 1.4 显示了打开应用程序时应该看到的内容。

图1.4现在我们已经提供了一个将输出和输入连接起来的服务器函数,我们已经有了一个功能完整的应用程序

请注意,每当您更改输入数据集时,摘要和表格都会自动更新。这种依赖性是通过在输出函数中引用 input$dataset 而隐式创建的。 input$dataset 被填充为 UI 组件中 id 为 dataset 的当前值,并且当该值发生变化时,将导致输出自动更新。这就是反应性的本质:输出在输入发生更改时自动(重新计算)。

使用反应式表达式减少重复

即使在这个简单的例子中,我们也有一些重复的代码:下面的这行代码在两个输出中都存在。

dataset <- get(input$dataset, "package:datasets")

在任何一种编程中,重复的代码都是不良的实践;它可能在计算上是浪费的,更重要的是,它增加了维护或调试代码的难度。在这里不是那么重要,但我想在一个非常简单的上下文中说明这个基本概念。

在传统的 R 脚本中,我们使用两种技术来处理重复的代码:要么捕获变量的值,要么用函数捕获计算。不幸的是,这两种方法在这里都不适用,原因将在第 13.2 节中介绍,我们需要一个新的机制: 反应式表达式

您可以通过将代码块包装在 reactive({...}) 中并将其分配给变量来创建反应式表达式,并且可以通过像调用函数一样来使用反应式表达式。然而,尽管看起来像是在调用函数,但反应式表达式有一个重要的区别:它只在其第一次被调用时运行,然后将其结果缓存直到需要更新。

我们可以更新我们的 server () 函数来使用反应式表达式,如下所示。这个应用程序的行为完全相同,但运行更有效率,因为它只需要检索数据集一次,而不是两次。

server <- function(input, output, session) {
  # Create a reactive expression
  dataset <- reactive({
    get(input$dataset, "package:datasets")
  })

  output$summary <- renderPrint({
    # Use a reactive expression by calling it like a function
    summary(dataset())
  })
  
  output$table <- renderTable({
    dataset()
  })
}

我们会多次回到响应式编程,但即使只有对输入、输出和反应式表达式的粗略了解,也可以构建非常有用的 Shiny 应用程序!

总结

在本章中,您创建了一个简单的应用程序 - 这不是很有趣或很有用,但是您已经看到了使用现有的 R 知识构建 Web 应用程序的容易程度。在接下来的两章中,您将学习更多关于用户界面和响应式编程的知识,这是 Shiny 的两个基本构建块。现在是获取 Shiny 速查表的绝佳时机。这是一个很好的资源,可以帮助您记住 Shiny 应用程序的主要组件。

图1.5 Shiny速查表,可从[https://www.rstudio.com/resources/cheatsheets/]获取

练习

1.8.1 创建一个应用程序,通过姓名向用户打招呼。您可能不知道需要使用哪些函数来完成此操作,因此我已经在下方包含了一些代码行。请考虑需要使用哪些行,然后将它们复制并粘贴到 Shiny 应用程序的适当位置。

tableOutput("mortgage")
output$greeting <- renderText({
  paste0("Hello ", input$name)
})
numericInput("age", "How old are you?", value = NA)
textInput("name", "What's your name?")
textOutput("greeting")
output$histogram <- renderPlot({
  hist(rnorm(1000))
}, res = 96)

1.8.2 假设你的朋友想设计一个应用程序,允许用户设置一个介于 1 和 50 之间的数字(x),并显示这个数字乘以 5 的结果。以下是他 / 她的第一次尝试:

library(shiny)

ui <- fluidPage(
  sliderInput("x", label = "If x is", min = 1, max = 50, value = 30),
  "then x times 5 is",
  textOutput("product")
)

server <- function(input, output, session) {
  output$product <- renderText({ 
    x * 5
  })
}

shinyApp(ui, server)

其不幸报错如下,请尝试修复错误:

error

1.8.3 将前一个练习中的应用程序扩展为允许用户设置乘数 y 的值,以便应用程序产生 x * y 的值。最终结果应如下所示:

xy

1.8.4 以下面的应用程序为例,它为上一练习中描述的最后一个应用程序添加了一些附加功能。有什么新功能?如何使用响应式表达式减少应用程序中的重复代码量。

library(shiny)

ui <- fluidPage(
  sliderInput("x", "If x is", min = 1, max = 50, value = 30),
  sliderInput("y", "and y is", min = 1, max = 50, value = 5),
  "then, (x * y) is", textOutput("product"),
  "and, (x * y) + 5 is", textOutput("product_plus5"),
  "and (x * y) + 10 is", textOutput("product_plus10")
)

server <- function(input, output, session) {
  output$product <- renderText({ 
    product <- input$x * input$y
    product
  })
  output$product_plus5 <- renderText({ 
    product <- input$x * input$y
    product + 5
  })
  output$product_plus10 <- renderText({ 
    product <- input$x * input$y
    product + 10
  })
}

shinyApp(ui, server)

1.8.5 以下应用程序与您在本章前面看到的应用程序非常相似:您从包中选择一个数据集(这次我们使用 ggplot2 包),应用程序会打印出数据的摘要和图表。它还遵循良好的实践,并利用响应式表达式来避免代码冗余。但是,下面提供的代码存在三个错误。您可以找到并修复它们吗?

library(shiny)
library(ggplot2)

datasets <- c("economics", "faithfuld", "seals")
ui <- fluidPage(
  selectInput("dataset", "Dataset", choices = datasets),
  verbatimTextOutput("summary"),
  tableOutput("plot")
)

server <- function(input, output, session) {
  dataset <- reactive({
    get(input$dataset, "package:ggplot2")
  })
  output$summmry <- renderPrint({
    summary(dataset())
  })
  output$plot <- renderPlot({
    plot(dataset)
  }, res = 96)
}

shinyApp(ui, server)

练习题答案获取

关注公众号 “生信之巅”,聊天窗口回复 “b376” 获取习题答案。

生信之巅微信公众号生信之巅小程序码

敬告:使用文中脚本请引用本文网址,请尊重本人的劳动成果,谢谢!Notice: When you use the scripts in this article, please cite the link of this webpage. Thank you!

上一篇:
16S扩增子测序及宏基因组测序
下一篇:
Shiny从入门到入定——0欢迎入坑
本文目录
本文目录