Подтвердить что ты не робот

Как организовать большие приложения 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)
4b9b3361

Ответ 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