如何组织大型Rshiny的应用程序?

什么是组织更大的shiny应用程序的最佳做法?
我认为最好的R实践也适用于R Shiny。
最佳R实践在这里讨论: 如何组织大型R程序
链接到谷歌的R风格指南: 风格指南

但是,我可以采用R Shiny上下文中的独特技巧和窍门,以使我的R代码看起来更好(更易读)。 我正在考虑像这样的事情:

  • 在R闪存中开发面向对象的程序devise
  • 在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中find了相当不错的解决scheme: 辐射代码
解决scheme是使用renderUI渲染每个tabPanel,并在server.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) 

我真的很喜欢Matt Leonawicz如何组织他的应用程序。 我采取了他的方法学习如何使用shiny,因为我们都知道,如果pipe理不善,它会变得非常分散。 看看他的结构,他总结了他在应用程序中组织应用程序run_alfresco的方式

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

在R光泽添加模块之后。 在有光泽的应用程序中pipe理复杂的结构变得容易很多。

shiny模块的详细描述: 在这里

使用模块的优点:

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

在基于标签的shiny的应用程序,一个选项卡可以被视为一个模块有input输出 。 标签的输出可以作为input传递给其他标签。

基于标签结构的单文件应用程序,利用模块化思维。 应用程序可以通过使用汽车数据集进行testing。 从Joe Cheng复制的部分代码(第一个链接)。 欢迎所有评论。

 # 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) 

我写了Radiant。 我还没有听到人们对代码组织的坏消息(但是),但我相信它可能会更好。 其中一个select就是将Joe的用法和逻辑分开,就像Joe Cheng在shiny的部分中所做的那样。

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

另一个可能是尝试OO编程,例如,使用R6 http://rpubs.com/wch/17459