R 本身是一个优秀的数据分析和数据可视化平台。然而,我们通常不会将 R 和分析脚
本提供给客户,让客户自己运行。数据分析的结果不仅可以在 HTML 网页、PDF 文档
或 Word 文档中显示,还可以呈现在交互式应用中,这种应用允许用户与数据进行交互,
用户可以修改某些参数并查看结果如何发生变化。
由 RStudio 开发的一个功能强大的扩展包 shiny(http://shiny.rstudio.com/) 就是专们为
此设计的。shiny 应用程序不同于我们之前所演示的交互式图形。它在 Web 浏览器中运行,
开发人员对网页中出现的内容,以及用户如何与它进行交互有完全的话语权。为了实现程
序功能,一个 shiny 应用程序基本上由两个重要部分组成:一个是与 Web 浏览器交互
的 HTTP 服务器,另一个是与 HTTP 服务器交互的 R 会话。
下面是一个最小的 shiny 应用程序。我们编写一个 R 脚本定义它的用户界面(ui)和
服务器(server)逻辑。用户界面是一个 boostrapPage,包含一个 numericInput 用
于接收一个整数表示样本容量,一个 textOutput 用于返回随机样本的均值。服务器
(server)背后的逻辑就是根据输入 input 的样本容量(n)生成随机数,计算随机样本的
均值,并放到输出结果 output 中:
library(shiny)
ui <- bootstrapPage(
numericInput("n", label = "Sample size", value = 10, min = 10, max =
100),
textOutput("mean")
)
server <- function(input, output) {
output$mean <- renderText(mean(rnorm(input$n)))
}
app <- shinyApp(ui, server)
runApp(app)
现在我们已经完成了定义,可以在 RStudio 中运行这段代码,展示这个最小的 shiny 应
用程序,运行结果如图 15-16 所示。
图 15-16
每次更改样本容量时,HTTP 服务器就会指令 R 后端重新运行一次,并更新输出的平
均值。
尽管前面的例子没有什么实质用途,但它演示了一个 shiny 应用程序的基本组件。现
在我们来看一个更复杂更有用的例子。
这个例子将几何布朗运动生成的许多路径进行可视化,几何布朗运动常用于股票价格建
模。我们知道,几何布朗运动的结果取决于初始值、预期增长率(r)、波动率(sigma)、持
续时间(T)和周期数(periods)。除了设定 T = 1 之外,我们允许用户修改其他所有参数。
现在,我们可以根据想要展示给用户的参数来定义 shiny 应用程序的用户界面。shiny 扩
展包提供了以下列示的丰富的输入控件:
shiny_vars <- ls(getNamespace("shiny"))
shiny_vars[grep("Input$", shiny_vars)]
## [1] "checkboxGroupInput" "checkboxInput"
## [3] "dateInput" "dateRangeInput"
## [5] "fileInput" "numericInput"
## [7] "passwordInput" "selectInput"
## [9] "selectizeInput" "sliderInput"
## [11] "textInput" "updateCheckboxGroupInput"
## [13] "updateCheckboxInput" "updateDateInput"
## [15] "updateDateRangeInput" "updateNumericInput"
## [17] "updateSelectInput" "updateSelectizeInput"
## [19] "updateSliderInput" "updateTextInput"
为了控制生成路径的随机性,我们允许用户指定随机数种子(seed),这样相同的种子
会生成相同的路径。定义用户界面的代码时,将 seed 作为 numericInput 的输入参数,
将其他输入参数作为 sliderInput 的参数。而且,sliderInput 控件有一定的范围和
步长,因此我们可以限定参数取值的合理范围。
用户界面不仅定义输入部分还定义输出部分,即在哪里显示什么内容。以下是 shiny 提
供的所有输出类型:
shiny_vars[grep("Output$", shiny_vars)]
## [1] "dataTableOutput" "htmlOutput"
## [3] "imageOutput" "plotOutput"
## [5] "tableOutput" "textOutput"
## [7] "uiOutput" "verbatimTextOutput"
在这个例子中,shiny 应用程序只是将所有路径放在一起,用于展示相同参数产生的各
种可能结果:
library(shiny)
ui <- fluidPage(
titlePanel("Random walk"),
sidebarLayout(
sidebarPanel(
numericInput("seed", "Random seed", 123),
sliderInput("paths", "Paths", 1, 100, 1),
sliderInput("start", "Starting value", 1, 10, 1, 1),
sliderInput("r", "Expected return", -0.1, 0.1, 0, 0.001),
sliderInput("sigma", "Sigma", 0.001, 1, 0.01, 0.001),
sliderInput("periods", "Periods", 10, 1000, 200, 10)),
mainPanel(
plotOutput("plot", width = "100%", height = "600px")
))
)
定义了用户界面以后,我们需要实现服务器的逻辑,它主要根据用户指定的参数生成
随机路径,并将它们放在同一幅图中。
以下代码是服务器逻辑的一个简单的实现。首先,设置随机数种子。然后,迭代调
用 sde::GBM,通过几何布朗运动生成随机路径。在调用 GBM 之前,要先运行 install.
packages("sde") 安装扩展包。
GBM 生成一条路径,sapply( )则将生成的所有路径组合成一个矩阵(mat),矩
阵的每一列都代表一条路径。最后,我们调用 matplot( ),在一幅图中用不同颜色区
分不同路径。
无论文本、图形还是表格,相关计算过程都是在 render* 函数中完成的。以下列出
shiny 扩展包提供的所有 render 函数:
shiny_vars[grep("^render", shiny_vars)]
## [1] "renderDataTable" "renderImage" "renderPage"
## [4] "renderPlot" "renderPrint" "renderReactLog"
## [7] "renderTable" "renderText" "renderUI"
在这个例子中,我们只需调用 renderPlot( ),将绘图代码放到函数中。当输入参
数被修改时,output$plot 函数将会转向用户界面中的 plotOutput("plot"):
server <- function(input, output) {
output$plot <- renderPlot({
set.seed(input$seed)
mat <- sapply(seq_ _len(input$paths), function(i) {
sde::GBM(input$start,
input$r, input$sigma, 1, input$periods)
})
matplot(mat, type = "l", lty = 1,
main = "Geometric Brownian motions")
})
}
现在,用户界面和服务器的逻辑都准备好了。我们把它们结合到一起,创建一个 shiny 应用
程序,并在 Web 浏览器中运行。
app <- shinyApp(ui, server)
runApp(app)
当参数被修改之后,绘图会自动更新,如图 15-17 所示。
图 15-17
如果设置了一个显著为正的预期年化收益,所生成的路径将趋于增长而非下降,
如图 15-18 所示。
图 15-18