4

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?

Liam Hash
  • 109
  • 1
  • 1
  • 4
  • 2
    Take a look at [How to create a good Shiny reproducible example?](https://stackoverflow.com/questions/48343080/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 Feb 06 '18 at 22:40
  • 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 Feb 07 '18 at 01:33
  • 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 like](https://gist.github.com/msummersgill/4d3a6a185b1e1eeeb2ff58c6c888475d) – Matt Summersgill Feb 07 '18 at 11:19

1 Answers1

0

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)
Matt Summersgill
  • 4,054
  • 18
  • 47
  • the BatchGetSymbols package takes a lot of stock information from the internet, which then is stored as its own set of data frames, which is why I needed to store it as l.out before taking l.out()$df.tickers, the data frame with the most pertinent stock info (closing prices, etc.). Basically, once I have l.out and stock_info <- l.out()$df.tickers, I am trying to create a simple way to add on just one column without editing the full data frame (basically there is an input that only one value requires this extra column, the stock returns). I can post the rest of the code if that is helpful – Liam Hash Feb 07 '18 at 01:38
  • Whenever I run the example in the [BatchGetSymbols vignette](https://cran.r-project.org/web/packages/BatchGetSymbols/vignettes/BatchGetSymbols-vignette.html) verbatim I am only getting error messages. If you could include a working `BatchGetSymbols()` call with hard-coded inputs that would be helpful as well. – Matt Summersgill Feb 07 '18 at 11:38
  • library(BatchGetSymbols) # set dates first.date <- Sys.Date() - 365 last.date <- Sys.Date() # set tickers tickers <- c("PYPL") l.out <- BatchGetSymbols(tickers = tickers, first.date = first.date, last.date = last.date ) is how that package usually works, with l.out$df.tickers having the most important stock information. the code i have for my shiny app is pretty extensive, since it has a lot of pieces. any chance i could message you the code for some help? – Liam Hash Feb 07 '18 at 23:01