mynameisJEFF
mynameisJEFF

Reputation: 4239

Shiny:How to avoid global variables and problem with ggplot one observation at a time

I am building a shiny application where every 30 secs, reactivefilereader reads in new data and process it by appending to data accumulated since the the app starts running (the data processing function append new data with existing aggregated data and returns one single row), and then ggplot will plot this single observation on the plot. And it will plot out the observations continuously with a line. However, I received this error message and nothing is being plotted on the shiny app.

geom_path: Each group consists of only one observation. Do you need to adjust the group aesthetic?

My data is as follow:

ts
                          Px
2020-03-13 17:15:19.000 23335.5

I have the global variable outside the server function. Note that below doesn't contain any data because the shiny will read in the data every 30 seconds.

ggprice   <-  ggplot() + scale_colour_manual(values=c(Px="black"))

And the plot is updated through below and ts is the data. And it will contain ONLY one observation.

ggprice   <<-    ggprice   + geom_line(aes(x=Index,y=Px,colour = "Px"),data=ts)

How do I overcome this ?

Update: A reproducible example as requested.

I have 2 global variables below, which i know are super clumsy.

  1. Global variable 1 - xts_bars_Agg used to store all the processed data since the app starts running
  2. Global variable 2 - ggprice. Every new observation is appended on this global variable by geom_line(...).

How can this be optimized ? Can the global variables be avoided here?

# Global variables
xts_bars_Agg <-  NULL


#   --- Function: Data I/O ------------------------------------------------------
data_processing <- function(csv_file){

    df                              <-  read.csv(csv_file, header=T,stringsAsFactors = F,colClasses = c("character","double"))
        # convert String to timestamp, remove string timestamp
        df                          <-  (data.frame( Timestamp = strptime(df[,1],"%Y-%m-%d %H:%M:%OS"), df[,c(-1)]))
        df_xts                      <-  xts(x=df[,c(-1)], order.by=df$Timestamp)
        xts_bars_Agg                <<- rbind.xts(xts_bars_Agg,df_xts)
        # *** the reason I need to rbind the new df_xts to 
        # existing xts_bars_agg (xts object) 
        # because the computeMagicalVal function below needs 
        # both the previous aggregated xts data plus the current xts data
        # to compute a value.
        # This and the usage of global variable looks very clumsy and inefficient and stupid to me. 
        # Is there a way to optimise this ?
        df_xts_final                 <- computeMagicalVal(xts_bars_Agg)

    # return df_xts_final with only one row, 
    # whereas xts_bars_Agg contains all the xts data with many rows
    return(df_xts_final)
}

# second global variable
# global variable
ggprice      <-  ggplot() +
                scale_colour_manual(values=c(Price="black"))

ggplot_func <- function(ts){
    ggprice      <<-    ggprice      + geom_line(aes(x=Index,y=Px,colour = "Price"),data=ts)
    return(ggprice)
}

# UI
ui <- fluidPage(
    #ggplot
    mainPanel(
        plotOutput(outputId = 'ggp')
    )
)

# Define server logic
server <- function(input, output, session) {    
    df_update <- reactiveFileReader(intervalMillis = 10000,session=NULL,
                                    filePath = "output.csv",
                                    readFunc = data_processing)

    output$ggp <- renderPlot({
        ggplot_func(df_update())
    })
}

# Run the application 
shinyApp(ui = ui, server = server)

Upvotes: 2

Views: 207

Answers (1)

Bertil Baron
Bertil Baron

Reputation: 5003

Hi you could try working with reactive Values like this

#   --- Function: Data I/O ------------------------------------------------------
data_processing <- function(csv_file){

  df                              <-  read.csv(csv_file, header=T,stringsAsFactors = F,colClasses = c("character","double"))
  # convert String to timestamp, remove string timestamp
  df                          <-  (data.frame( Timestamp = strptime(df[,1],"%Y-%m-%d %H:%M:%OS"), df[,c(-1)]))
  df_xts                      <-  xts(x=df[,c(-1)], order.by=df$Timestamp)


  return(df_xts)
}

# UI
ui <- fluidPage(
  #ggplot
  mainPanel(
    plotOutput(outputId = 'ggp')
  )
)

# Define server logic
server <- function(input, output, session) {

  rective_values <- reactiveValues(
    xts_bars_Agg = tibble(),
    ggprice = ggplot() +
      scale_colour_manual(values=c(Price="black"))
  )

  # whereas xts_bars_Agg contains all the xts data with many rows
  observe({
    reactive_values$xts_bars_Agg = rbind.xts(xts_bars_Agg,df_update()) 
    # *** the reason I need to rbind the new df_xts to 
    # existing xts_bars_agg (xts object) 
    # because the computeMagicalVal function below needs 
    # both the previous aggregated xts data plus the current xts data
    # to compute a value.
    # This and the usage of global variable looks very clumsy and inefficient and stupid to me. 
    # Is there a way to optimise this ?
  })

  observeEvent(reactive_values$xts_bars_Agg,{
    df_xts_final = computeMagicalVal(reactive_values$xts_bars_Agg)
    reactive_values$ggprice = reactive_values$ggprice + 
      geom_line(aes(x=Index,y=Px,colour = "Price"),
                data= df_xts_final)
  })
  df_update <- reactiveFileReader(intervalMillis = 10000,session=NULL,
                                  filePath = "output.csv",
                                  readFunc = data_processing)



  output$ggp <- renderPlot({
    reactive_values$ggprice
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

Hope this helps!!

Upvotes: 1

Related Questions