【问题标题】:R shiny making my inputs reactive to parameters in a functionR闪亮使我的输入对函数中的参数产生反应
【发布时间】:2020-10-28 04:04:41
【问题描述】:

我正在尝试与 R Shiny 一起玩耍并了解更多信息。我开发了一个反应式用户界面,但我对如何实现用户选择有点卡住。

我希望用户单击“添加文本框”按钮,从下拉菜单中选择一个变量/函数并将输入应用于该函数。为简单起见,我使用 TTR 包中的 SMA 和 WMA 以及 quantmod 来收集数据。

SMA 只接受一个输入 n,而 WMA 可以接受两个输入 n 和 weights。我设法使 Shiny 应用程序根据用户选择的选择做出反应,但我现在想将这些用户选择应用于数据。也就是说,我希望能够根据用户的功能选择和输入向表中添加新列。

预期的输出将能够得到一个带有额外列的表

MSFT.Open   MSFT.High   MSFT.Low    MSFT.Close  MSFT.Volume MSFT.Adjusted  SMA.2, SMA.10, WMA.10
46.66   47.42   46.54   46.76   27913900.00 41.75                           NA      NA      NA
46.37   46.73   46.25   46.33   39673900.00 41.36                           NA      NA      NA                       
46.38   46.75   45.54   45.65   36447900.00 40.76                           999     NA      NA
45.98   46.46   45.49   46.23   29114100.00 41.28                           999     NA      NA
46.75   47.75   46.72   47.59   29645200.00 42.49                           999     NA      NA

(这里数据的head 将包含除 SMA.2 列之外的 NA)。我认为用户接口很好(如果我错了,请纠正我),我现在需要插入并应用到server 函数。

我希望用户可以根据需要添加任意数量的SMA 和WMA 函数(和列)。

R 代码:

downloadFinancialData <- function(symbol, start, end){
  data <- getSymbols(Symbols = symbol, src = "yahoo", index.class = "POSIXct", from = start, to = end, auto.assign = FALSE)
  # we can compute the returns and some other things inside this function so we can later plot for the user.
}


symbol = "MSFT"
start = "2018-01-01"
end =  "2019-01-01"
data = downloadFinancialData(symbol = symbol, start = start, end = end)

n = 10
SMA(Cl(data), n = n)
WMA(Cl(data), n = n, wts = 1:n)
WMA(Cl(data), n = n, wts = rep(weights, times = nrow(data)))

闪亮的代码:

library(shiny)
library(quantmod)

dist <- c("SMA", "WMA")
add_box <- function(id){
    ns <- NS(id)
    tags$div(id = paste0("indicatorChoiceBox", id),
             selectInput(inputId = ns("indicatorChoiceSelection"),
                         label = paste0("Variable ", id),
                         choices = dist),

             conditionalPanel(
                 condition = "input.indicatorChoiceSelection=='SMA'",
                 ns = ns,
                 column(width = 3, numericInput(ns('nSMAPeriodSelection'), 'Number of Periods', value = '0'))
             ),
             conditionalPanel(
                 condition =  "input.indicatorChoiceSelection=='WMA'",
                 ns = ns,
                 column(width = 3, numericInput(ns('nWMAPeriodSelection'), 'Number of Periods', value = '0')),
                 column(width = 3, numericInput(ns('weightsWMAPeriodSelection'), 'Weights', value = '0'))
             )
    )
}


downloadFinancialData <- function(symbol, start, end){
    data <- getSymbols(Symbols = symbol, src = "yahoo", index.class = "POSIXct", from = start, to = end, auto.assign = FALSE)
    # we can compute the returns and some other things inside this function so we can later plot for the user.
}

#####################################################################################
ui <- shinyUI(fluidPage(

    sidebarPanel(
        # 1.a) Collect financial data:
        wellPanel(
            textInput(inputId = "symbolInput", label = "Symbol", value = "MSFT"),
            dateRangeInput(inputId = "stockDateRange", label = "Dates", start = "2015-01-01", end = "2018-01-01")
        ),

        actionButton("addIndicator", "Add Textbox"),
        actionButton("rmIndicator", "Remove Textbox"),
        textOutput("counter")

    ),

    mainPanel(
        tableOutput("stockData"),
        column(width = 12, id = "column")
        )

))

server <- shinyServer(function(input, output, session) {
    
    ###########################################
    # 1.a) Process financial data:
    stockData <- reactive({
        symbol = input$symbolInput
        start = input$stockDateRange[1]
        end = input$stockDateRange[2]
        
        data = downloadFinancialData(symbol = symbol, start = start, end = end)
        
        # Modify data here depending on the users function selection and value input

    })
    
    output$stockData <- renderTable({
        hd <- head(stockData())
        tl <- tail(stockData())
        
        out <- rbind(hd, tl)
        
    })
    ###########################################
    
    ###########################################
    # Track the number of input boxes to render
    counter <- reactiveValues(n = 0)

    # Track all user inputs
    AllInputs <- reactive({
        x <- reactiveValuesToList(input)
    })

    observeEvent(input$addIndicator, {
        counter$n <- counter$n + 1
        insertUI(selector = "#column",
                 where = "beforeEnd",
                 ui = add_box(counter$n)
        )
    })

    observeEvent(input$rmIndicator, {
        if (counter$n > 0) {
            removeUI(selector = paste0("#indicatorChoiceBox", counter$n))
            counter$n <- counter$n - 1
        }
    })

    output$counter <- renderPrint(print(counter$n))
    ###########################################

})

shinyApp(ui, server)

【问题讨论】:

    标签: r shiny


    【解决方案1】:

    这是一个可行的解决方案(实际表格输出除外)。我选择了以下策略:不仅使用模块添加/删除 UI,还添加/删除服务器逻辑。添加的每个模块都有自己的逻辑来将所选函数应用于数据并返回结果。因此,我修改了您的模块代码如下:

    add_box_UI <- function(id){
      ns <- NS(id)
      tags$div(id = paste0("indicatorChoiceBox", id),
               selectInput(inputId = ns("indicatorChoiceSelection"),
                           label = paste0("Variable ", id),
                           choices = dist),
               actionButton(inputId = ns("calculate_results"),
                            label = "Calculate results"),
               
               conditionalPanel(
                 condition = "input.indicatorChoiceSelection=='SMA'",
                 ns = ns,
                 column(width = 3, numericInput(ns('nSMAPeriodSelection'), 'Number of Periods', value = '0'))
               ),
               conditionalPanel(
                 condition =  "input.indicatorChoiceSelection=='WMA'",
                 ns = ns,
                 column(width = 3, numericInput(ns('nWMAPeriodSelection'), 'Number of Periods', value = '0')),
                 column(width = 3, numericInput(ns('weightsWMAPeriodSelection'), 'Weights', value = '0'))
               )
      )
    }
    
    add_box <- function(id, data) {
      moduleServer(
        id,
        function(input, output, session) {
          results <- reactiveVal(NULL)
          observeEvent(input$calculate_results, {
            if (input$indicatorChoiceSelection == "SMA") {
              results(SMA(Cl(data), n = input$nSMAPeriodSelection))
            }
            
            if (input$indicatorChoiceSelection == "WMA") {
              results(WMA(Cl(data), n = n = input$nSMAPeriodSelection,
                          wts = rep(input$weightsWMAPeriodSelection,
                                    times = nrow(data))))
            }
          })
          
          return(results)
        }
      )
    }
    
    • 我添加了actionButton 来计算结果
    • 在模块的服务器端,observeEvent 监听此按钮并执行所选功能
    • 结果以reactiveValue 的形式返回。请注意,我返回 results 而不是 results()。通过使用results,该值仍被识别为调用环境中的反应值

    现在,当您添加 UI 元素时,您还必须添加模块的服务器逻辑。为此,您可以只使用模块的名称add_box。请注意,您需要 Shiny 1.5.0。我将调用模块的所有输出存储在reactiveVal 中,列表名为module_results。当调用或删除更多模块时,您可以从列表中添加/删除条目。我使用了reactiveVal 而不是reactiveValues,因为后者本身不是反应式的,所以当其中一个模块的输出发生变化时不会触发stockData,只有在添加/删除模块时才会触发。现在您可以使用module_results 将结果添加到您的原始data.frame。由于我不熟悉您的数据结构,因此我将其留空:

    server <- shinyServer(function(input, output, session) {
      
      ###########################################
      # 1.a) Process financial data:
      stockData <- reactive({
        symbol = input$symbolInput
        start = input$stockDateRange[1]
        end = input$stockDateRange[2]
        
        data = downloadFinancialData(symbol = symbol, start = start, end = end)
        
        # Modify data here depending on the users function selection and value input
        
        
        # execute the reactiveValues to a normal value
        add_data <- lapply(module_results(), function(x) x())
        # check which data is not NULL
        index_data <- unlist(lapply(add_data, function(x) !is.null(x)))
        if (sum(index_data) > 0) {
          # do something with the data here
        }
        
        data
        
      })
      
      data_basis <- reactive({
        symbol = input$symbolInput
        start = input$stockDateRange[1]
        end = input$stockDateRange[2]
        
        data = downloadFinancialData(symbol = symbol, start = start, end = end)
        data
        
      })
      
      output$stockData <- renderTable({
        hd <- head(stockData())
        tl <- tail(stockData())
        
        out <- rbind(hd, tl)
        
      })
      ###########################################
      
      ###########################################
      # Track the number of input boxes to render
      counter <- reactiveValues(n = 0)
      
      # store the results of the called modules
      module_results <- reactiveVal(list())
      
      # Track all user inputs
      AllInputs <- reactive({
        x <- reactiveValuesToList(input)
      })
      
      observeEvent(input$addIndicator, {
        counter$n <- counter$n + 1
        insertUI(selector = "#column",
                 where = "beforeEnd",
                 ui = add_box_UI(counter$n)
        )
        
        # add the server logic
        temp <- module_results()
        temp[[as.character(counter$n)]] <-
          add_box(as.character(counter$n), data_basis())
        module_results(temp)
      })
      
      observeEvent(input$rmIndicator, {
        if (counter$n > 0) {
          removeUI(selector = paste0("#indicatorChoiceBox", counter$n))
          temp <- module_results()
          temp[[counter$n]] <- NULL
          module_results(temp)
          counter$n <- counter$n - 1
        }
      })
      
      output$counter <- renderPrint(print(counter$n))
      ###########################################
      
    })
    

    显然,添加所有观察者可以lead to problems ultimately,因此您可以考虑在之后删除它们。我还没有自己测试过。

    编辑

    我将reactiveValues改为reactiveVal作为存储变量类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-17
      • 2020-07-04
      • 2020-08-01
      • 2020-08-18
      • 1970-01-01
      • 1970-01-01
      • 2017-11-24
      相关资源
      最近更新 更多