Как организовать большие приложения R Shiny?
Каковы наилучшие методы организации более крупных блестящих приложений?
Я считаю, что лучшие методы R применимы и к R Shiny.
Лучшие практики R обсуждаются здесь: Как организовать большие программы R
Ссылка на руководство по стилю Google R: Руководство по стилю
Но каковы уникальные советы и трюки в контексте R Shiny, которые я могу принять, чтобы сделать мой R-блестящий код лучше (и более читаемым)?
Я думаю о таких вещах, как:
- Использование объектно-ориентированного программирования в R блестящем
- В server.R какие части должны быть получены?
- Иерархия файлов проекта, содержащая документы, рисунки,
xml и исходные файлы
Например, если я использую navbarPage
и tabsetPanel
в каждом tabPanel
, мой код начинает выглядеть довольно беспорядочным после добавления нескольких элементов пользовательского интерфейса.
Пример кода:
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 для отображения каждой вкладки tabPanel и на сервере .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)
Ответы
Ответ 1
Мне очень нравится, как Мэтт Леонавич организует свои приложения. Я взял его подход, изучая, как использовать Блестящий, поскольку все мы знаем, что он может стать совершенно разбросанным, если не управлять должным образом. Посмотрите на его структуру, он дает обзор того, как он организует приложения в приложении под названием run_alfresco
https://github.com/ua-snap/shiny-apps
Ответ 2
После добавления модулей в R блестящий. Управление сложными структурами в блестящих приложениях стало намного проще.
Подробное описание блестящих модулей: Здесь
Преимущества использования модулей:
- После создания они легко используются повторно
- Коллизии идентификаторов легче избежать.
- Организация кода на основе ввода и вывода модулей
В ярком приложении на основе вкладок одна вкладка может рассматриваться как один модуль, который имеет входы и выходы. Выходы вкладок могут быть затем переданы на другие вкладки в качестве входных данных.
Однофайльное приложение для табуляции, использующее модульное мышление. Приложение можно протестировать с помощью 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 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)
Ответ 3
Я написал Radiant. Я не слышал, чтобы люди плохо говорили о организации кода (пока), но я уверен, что это может быть лучше. Один из вариантов заключается в том, чтобы отделить ui и логику, как это делает Джо Ченг в блестящих частицах.
https://github.com/jcheng5/shiny-partials
Другим может быть попытка программирования OO, например, с использованием R6 http://rpubs.com/wch/17459