组织更大的Shiny应用程序的最佳做法是什么?
我认为最好的R实践也适用于Shiny
这里讨论最佳R实践:How to organize large R programs
链接到Google的R风格指南:Style Guide
但是我可以采用什么样的Shiny上下文中的独特提示和技巧来使我的Shiny代码看起来更好(并且更具可读性)? 我想的是:
server.R
哪些部分应该来源?例如,如果我在每个navbarPage
中使用tabsetPanel
和tabPanel
,则在添加多个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找到了很好的解决方案:radiant code
解决方案是使用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)
答案 0 :(得分:23)
我真的很喜欢Matt Leonawicz如何组织他的应用程序。我采用他的方法学习如何使用Shiny,因为我们都知道,如果管理不当,它会变得非常分散。看看他的结构,他概述了他在应用程序中组织应用程序的方式,名为 run_alfresco
答案 1 :(得分:22)
将模块添加到R闪亮之后。在闪亮的应用程序中管理复杂结构变得更加容易。
闪亮模块的详细说明:Here
使用模块的优点:
- 创建后,它们很容易重复使用
- ID碰撞更容易避免
- 基于模块输入和输出的代码组织
在基于标签的闪亮应用中,可以将一个标签视为一个具有输入和输出的模块。然后可以将选项卡的输出作为输入传递给其他选项卡。
基于标签的结构的单文件应用程序,它利用模块化思维。可以使用cars数据集测试应用程序。从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)
答案 2 :(得分:7)
我写了Radiant。我还没有听到人们说代码组织的坏事(但是)但我相信它会更好。一种选择是将ui和逻辑分开,就像Joe Cheng在闪亮的部分中所做的那样。
https://github.com/jcheng5/shiny-partials
另一种可能是尝试OO编程,例如,使用R6 http://rpubs.com/wch/17459
答案 3 :(得分:2)
现在还有golem
软件包,它提供了用于组织闪亮代码的框架。它主要使用模块,但也提供了如何组织结构的结构,例如辅助函数和CSS / JavaScript文件。还有一个accompanying book。