renderprint - ¿Cómo organizar grandes aplicaciones brillantes?
shiny dashboard (3)
¿Cuáles son las mejores prácticas para organizar aplicaciones Shiny más grandes?
Creo que las mejores prácticas de R también son aplicables a Shiny.
Las mejores prácticas de R se analizan aquí:
Cómo organizar grandes programas de R
Enlace a la Guía de estilo R de Google:
Guía de estilo
Pero, ¿cuáles son los consejos y trucos únicos en el contexto Shiny que puedo adoptar para que mi código Shiny se vea mejor (y más legible)? Estoy pensando en cosas como:
- Explotar la programación orientada a objetos en Shiny
-
En
server.R
qué partes deben obtenerse? - Jerarquía de archivos del proyecto que contiene documentos de descuento, imágenes, xml y archivos fuente
Por ejemplo, si estoy usando
navbarPage
y
tabsetPanel
en cada
tabPanel
mi código comienza a verse bastante desordenado después de agregar varios elementos de la interfaz de usuario.
Código de ejemplo:
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)
Para organizar el código
ui.R
encontré una solución bastante buena de GitHub:
código radiante
La solución es usar
renderUI
para renderizar cada
tabPanel
y en el
server.R
pestañas
server.R
se obtienen de diferentes archivos.
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)
Después de la adición de módulos a R brillante. La gestión de estructuras complejas en aplicaciones brillantes se ha vuelto mucho más fácil.
Descripción detallada de módulos brillantes: Here
Ventajas de usar módulos:
- Una vez creados, se reutilizan fácilmente
- Las colisiones de identificación son más fáciles de evitar
- Organización del código basada en entradas y salidas de módulos.
En la aplicación brillante basada en pestañas, una pestaña puede considerarse como un módulo que tiene entradas y salidas . Las salidas de las pestañas se pueden pasar a otras pestañas como entradas.
Aplicación de archivo único para estructura basada en pestañas que explota el pensamiento modular. La aplicación se puede probar utilizando el conjunto de datos de cars . Partes del código se copiaron del Joe Cheng (primer enlace). Todos los comentarios son bienvenidos.
# 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)
Escribí Radiant. No he escuchado a la gente decir cosas malas sobre la organización del código (todavía), pero estoy seguro de que podría ser mejor. Una opción sería separar la interfaz de usuario y la lógica como lo hace Joe Cheng en parciales brillantes.
https://github.com/jcheng5/shiny-partials
Otra podría ser probar la programación OO, por ejemplo, usando R6 http://rpubs.com/wch/17459
Realmente me gusta cómo Matt Leonawicz organiza sus aplicaciones. Tomé su enfoque aprendiendo a usar Shiny, ya que todos sabemos que puede dispersarse bastante si no se maneja adecuadamente. Eche un vistazo a su estructura, da una visión general de la forma en que organiza las aplicaciones en la aplicación llamada run_alfresco