4
votes

within the server for my shinyApp, I created a dataframe based on the inputs. However, I want to add a new column that utilizes two of the columns of that dataframe.

server <- function(input, output, session) {
  l.out <- reactive({
    BatchGetSymbols(tickers = input$stock, 
                first.date = Sys.Date() - as.integer(input$length),
                last.date = Sys.Date())
  })
  stock_info <- reactive({
    l.out()$df.tickers
  })
  stock_info()$return <- reactive({
    rep(0, length(stock_info()$ref.date))
  })
  stock_info()$return <- reactive({
    for (i in 2:length(stock_info()$ref.date)){
      stock_info()$return[i] <- ((stock_info()$price.close[i] - 
stock_info()$price.close[i - 1]) / stock_info$price.close[i - 1])
    }
  })

I have tried it like this, and it works up until I try to create stock_info()$return, where I keep getting the error that NULL left assignment. Any tips?

1
Take a look at How to create a good Shiny reproducible example?, if you can update your example based on those recommendations that would help others help you!Matt Summersgill
I could post the full code if that makes things easier. Obviously the UI code and a lot of the excess server code won't be applicable, but it will give a better understanding of the full picture.Liam Hash
Your original instincts were correct that posting the full code would muddy the water with extraneous information. Example code should include loading any packages used -- library(shiny), library(BatchGetSymbols), etc.. Example ui would ideally only have input$stock, input$length (with working default values), and a table output printing your desired result dataframe, stock_info. Example server code you have is sufficient (even though it doesn't work). Here's a gist of what it might look likeMatt Summersgill

1 Answers

0
votes

I'm not familiar with the BatchGetSymbols package, but the concepts in the example below should be applicable for your use case as well.

First things first, for lack of an elegant way to say this, I'm pretty sure the expression...

  stock_info()$return <- reactive({
    rep(0, length(stock_info()$ref.date))
  })

...just isn't really how shiny reactive objects and the associated syntax work.

It looks like you could simplify your code a lot by condensing a bunch of your intermediate steps into a single expression. If you only have one set of reactive data you will use in all of your outputs, this might be a more straight forward approach.

library(shiny)

ui <- fluidPage(
  textInput('stock','stock',"GE"),
  sliderInput('length', 'length', min = 1, max = 10, value = 5),
  dataTableOutput('my_table')
)

server <- function(input, output, session) {

  ## This will update whenever either input$length or input$stock change
  stock_info <- reactive({

    length <- as.integer(input$length)

    temp_stock_info <- data.frame(stock = input$stock,
                                  foo = seq_len(length),
                                  bar = rnorm(length))

    temp_stock_info$baz <- paste("xxx",length)

    return(temp_stock_info)
  })

  ## Return an output
  output$my_table <- renderDataTable({
    stock_info()
  })
}

shinyApp(ui, server)

However, if you are using the intermediate object l.out for a variety of end outputs, it might make sense to make it a reactive object of it's own. Then, we can update l.out whenever a relevant input changes, and then use that intermediate variable to cascade updates through the other downstream reactives.

In addition, we can update downstream reactive objects like stock_info based on other conditions that don't affect l.out without re-running l.out every time.

library(shiny)

ui <- fluidPage(
  textInput('stock','stock',"GE"),
  sliderInput('length', 'length', min = 1, max = 100, value = 50),
  sliderInput('displayLength', 'displayLength', min = 1, max = 20, value = 5),
  dataTableOutput('my_table')
)

server <- function(input, output, session) {

  ## l.out will change with input$length and input$stock
  ## but NOT input$displayLength
  l.out <- reactive({
    data.frame(stock = input$stock,
               foo = rnorm(input$length),
               l.out_update_time = Sys.time())
  })

  ## stock_info will update whenever l.out changes or the displayLength changes. 
  ## l.out will NOT be updated if only input$displayLength changes
  stock_info <- reactive({
    tmp_stock_info <- head(x = l.out(), n = input$displayLength)
    tmp_stock_info$stock_info_update_time <- Sys.time()
    return(tmp_stock_info)
  })

  ## Return an output
  output$my_table <- renderDataTable({
    stock_info()
  })
}

shinyApp(ui, server)