如何组织大型 Shiny 应用程序?

组织更大的 Shiny 应用程序的最佳实践是什么?
我认为最佳 R 实践也适用于 Shiny。
这里讨论了最佳 R 实践: 如何组织大型 R 程序
链接到 Google 的 R Style Guide: 样式指南

但是,在 Shiny 上下文中,我可以采用哪些独特的提示和技巧来使我的 Shiny 代码看起来更好(更具可读性)呢? 我在想这样的事情:

  • 在 Shiny 中开发面向对象编程
  • server.R中应该采购哪些零件?
  • 项目的文件层次结构,包含标记文件,图片, Xml 和源文件

例如,如果我在每个 tabPanel中都使用 navbarPagetabsetPanel,那么在添加了几个 UI 元素之后,我的代码看起来就会非常混乱。

示例代码:

server <- function(input, output) {


#Here functions and outputs..


}


ui <- shinyUI(navbarPage("My Application",
tabPanel("Component 1",
sidebarLayout(
sidebarPanel(
# UI elements..
),
mainPanel(
tabsetPanel(
tabPanel("Plot", plotOutput("plot")
# More UI elements..
),
tabPanel("Summary", verbatimTextOutput("summary")
# And some more...
),
tabPanel("Table", tableOutput("table")
# And...
)
)
)
)
),
tabPanel("Component 2"),
tabPanel("Component 3")
))


shinyApp(ui = ui, server = server)

对于组织 ui.R代码,我从 GitHub 中找到了相当不错的解决方案: 辐射代码
解决方案是使用 renderUI来渲染每个 tabPanelserver.R标签页都来源于不同的文件。

server <- function(input, output) {


# This part can be in different source file for example component1.R
###################################
output$component1 <- renderUI({
sidebarLayout(
sidebarPanel(
),
mainPanel(
tabsetPanel(
tabPanel("Plot", plotOutput("plot")),
tabPanel("Summary", verbatimTextOutput("summary")),
tabPanel("Table", tableOutput("table"))
)
)
)
})
#####################################


}
ui <- shinyUI(navbarPage("My Application",
tabPanel("Component 1", uiOutput("component1")),
tabPanel("Component 2"),
tabPanel("Component 3")
))


shinyApp(ui = ui, server = server)
19532 次浏览

我写了 Radiant。我还没有听到人们说代码组织的坏话(还没有) ,但我确信它可以做得更好。一种选择是分离 ui 和逻辑,就像 Joe Cheng 在亮部分中所做的那样。

Https://github.com/jcheng5/shiny-partials

另一种方法是尝试面向对象编程,例如,使用 R6 http://rpubs.com/wch/17459

我真的很喜欢 Matt Leonawicz 组织应用程序的方式。我采用了他的方法学习如何使用 Shiny,因为我们都知道,如果管理不当,它可能会变得相当分散。看看他的结构,他给出了一个概述的方式,他组织的应用程序中的应用程序称为 Run _ alfresco

Https://github.com/ua-snap/shiny-apps

在增加了 模组到 R 的光泽之后,在光泽的应用程序中管理复杂的结构变得容易多了。

发光模块的详细描述: 给你

使用模块的优点:

  • 一旦创建,它们就很容易重用
  • ID 冲突更容易避免
  • 基于模块输入输出的代码组织

在标签为基础的闪亮的应用程序,一个标签可以被视为一个模块,其中有 输入输出。然后,选项卡的输出可以作为输入传递给其他选项卡。

利用模块化思想的基于制表符结构的单文件应用程序。应用程序可以通过使用 汽车数据集进行测试。部分代码从何处抄袭成(第一个链接)。欢迎各界提出意见。

# Tab module
# This module creates new tab which renders dataTable


dataTabUI <- function(id, input, output) {
# Create a namespace function using the provided id
ns <- NS(id)


tagList(sidebarLayout(sidebarPanel(input),


mainPanel(dataTableOutput(output))))


}


# Tab module
# This module creates new tab which renders plot
plotTabUI <- function(id, input, output) {
# Create a namespace function using the provided id
ns <- NS(id)


tagList(sidebarLayout(sidebarPanel(input),


mainPanel(plotOutput(output))))


}


dataTab <- function(input, output, session) {
# do nothing...
# Should there be some logic?




}


# File input module
# This module takes as input csv file and outputs dataframe
# Module UI function
csvFileInput <- function(id, label = "CSV file") {
# Create a namespace function using the provided id
ns <- NS(id)


tagList(
fileInput(ns("file"), label),
checkboxInput(ns("heading"), "Has heading"),
selectInput(
ns("quote"),
"Quote",
c(
"None" = "",
"Double quote" = "\"",
"Single quote" = "'"
)
)
)
}


# Module server function
csvFile <- function(input, output, session, stringsAsFactors) {
# The selected file, if any
userFile <- reactive({
# If no file is selected, don't do anything
validate(need(input$file, message = FALSE))
input$file
})


# The user's data, parsed into a data frame
dataframe <- reactive({
read.csv(
userFile()$datapath,
header = input$heading,
quote = input$quote,
stringsAsFactors = stringsAsFactors
)
})


# We can run observers in here if we want to
observe({
msg <- sprintf("File %s was uploaded", userFile()$name)
cat(msg, "\n")
})


# Return the reactive that yields the data frame
return(dataframe)
}
basicPlotUI <- function(id) {
ns <- NS(id)
uiOutput(ns("controls"))


}
# Functionality for dataselection for plot
# SelectInput is rendered dynamically based on data


basicPlot <- function(input, output, session, data) {
output$controls <- renderUI({
ns <- session$ns
selectInput(ns("col"), "Columns", names(data), multiple = TRUE)
})
return(reactive({
validate(need(input$col, FALSE))
data[, input$col]
}))
}


##################################################################################
# Here starts main program. Lines above can be sourced: source("path-to-module.R")
##################################################################################


library(shiny)




ui <- shinyUI(navbarPage(
"My Application",
tabPanel("File upload", dataTabUI(
"tab1",
csvFileInput("datafile", "User data (.csv format)"),
"table"
)),
tabPanel("Plot", plotTabUI(
"tab2", basicPlotUI("plot1"), "plotOutput"
))


))




server <- function(input, output, session) {
datafile <- callModule(csvFile, "datafile",
stringsAsFactors = FALSE)


output$table <- renderDataTable({
datafile()
})


plotData <- callModule(basicPlot, "plot1", datafile())


output$plotOutput <- renderPlot({
plot(plotData())
})
}




shinyApp(ui, server)

现在还有 golem包,它提供了一个组织闪亮代码的框架。它主要使用模块,但也提供了一个如何组织帮助函数和 css/javascript 文件的结构。还有一个 附带的书

我已经有一段时间没有创建这个解决方案了,但我最终还是在罗奇和基因泰克开源公司完成了我的工作。当我们在开发一个大的闪亮的应用程序时,我们不能使用闪亮的模块(如果我没记错的话,因为模块不允许我们共享数据) ,我想出了一个基于组件的架构,灵感主要来自 AngularJS。

该项目的回购所谓的电池可在 GitHub 上的基因技术组织

在这里,您可以阅读一个教程,解释如何使用框架。

非平凡 R 闪亮应用的体系结构