Search code examples
rshinyplotlyr-plotly

How to do live updates on an embedded plotly R graph


I just made my first interactive plotly graph in R. Now I would like to embed it in a blog post using iframes and have the data and the graph automatically update from an api at regular intervals. What is the best way to do that? Specifically, where can I host the plot? Is there a way to do it with shiny or RStudio Connect?

Update:

I have been trying to model my code based on the example provided by @ismirsehregal below. I can't figure out how to set xData and YData. I have the date on the x axis, so should the initial value of xData be set to the date variable? Should the initial value of y be set at NULL as in the example? Etc. Here is what I have so far:

library(Quandl)
library(plotly)
library(shiny)

theme_set(theme_minimal())

ui <- fluidPage(
  plotlyOutput("yield_chart")
)

server <- function(input, output, session) {
  
  yield_chart <- plot_ly(DF, x = ~date, y = ~ten_yr, name = '10 Yr.', type = 'scatter', mode = 'lines', line=list(color='darkred') 
  )
  
  output$linePlot <- renderPlotly({
    yield_chart
  })
  
  lineProxy <- plotlyProxy("yield_chart", session)
  
  xData <- reactiveVal(DF$date)
  yData <- reactiveVal(NULL)
  
  observe({
    invalidateLater(1000)
    treas_yields <- Quandl("USTREASURY/YIELD", api_key="123xyz")
    DF <- treas_yields[, c("Date", "10 YR")]
    colnames(DF) <- c("date", "ten_yr") # change var names
    DF <- subset(DF, ten_yr!= 0)# drop rows with 0 values (e.g. April 14, 2017)
    isolate(xData(xData()+1))
    yData(DF$ten_yr)
  })
  
  observe({
    plotlyProxyInvoke(lineProxy, "extendTraces", list(x = list(list(xData())), y = list(list(yData()))), list(0))
  })
  
}

shinyApp(ui, server)    

Here are the first 100 rows of data:

structure(list(date = structure(c(19006, 19005, 19004, 19003, 
19002, 18999, 18998, 18997, 18996, 18995, 18992, 18991, 18990, 
18989, 18988, 18984, 18983, 18982, 18981, 18978, 18977, 18976, 
18975, 18974, 18971, 18970, 18969, 18968, 18967, 18964, 18963, 
18962, 18961, 18960, 18957, 18955, 18954, 18953, 18950, 18949, 
18948, 18947, 18946, 18943, 18941, 18940, 18939, 18936, 18935, 
18934, 18933, 18932, 18929, 18928, 18927, 18926, 18925, 18922, 
18921, 18920, 18919, 18918, 18915, 18914, 18913, 18912, 18908, 
18907, 18906, 18905, 18904, 18901, 18900, 18899, 18898, 18897, 
18894, 18893, 18892, 18891, 18890, 18887, 18886, 18885, 18884, 
18883, 18880, 18879, 18878, 18877, 18873, 18872, 18871, 18870, 
18869, 18866, 18865, 18864, 18863, 18862), class = "Date"), ten_yr = c(1.78, 
1.7, 1.74, 1.75, 1.78, 1.76, 1.73, 1.71, 1.66, 1.63, 1.52, 1.52, 
1.55, 1.49, 1.48, 1.5, 1.46, 1.48, 1.43, 1.41, 1.44, 1.47, 1.44, 
1.42, 1.48, 1.49, 1.52, 1.48, 1.43, 1.35, 1.44, 1.43, 1.43, 1.52, 
1.48, 1.64, 1.67, 1.63, 1.54, 1.59, 1.6, 1.63, 1.63, 1.58, 1.56, 
1.46, 1.51, 1.45, 1.53, 1.6, 1.56, 1.58, 1.55, 1.57, 1.54, 1.63, 
1.64, 1.66, 1.68, 1.65, 1.65, 1.59, 1.59, 1.52, 1.56, 1.59, 1.61, 
1.58, 1.53, 1.54, 1.49, 1.48, 1.52, 1.55, 1.54, 1.48, 1.47, 1.41, 
1.32, 1.33, 1.31, 1.37, 1.34, 1.31, 1.28, 1.33, 1.35, 1.3, 1.35, 
1.38, 1.33, 1.29, 1.31, 1.3, 1.29, 1.31, 1.34, 1.35, 1.29, 1.25
)), row.names = c(NA, 100L), class = "data.frame")

Solution

  • The following shows how to use plotlyProxyInvoke to call plotlys "extendTraces" JS function to add a datapoint to the plot each second:

    library(shiny)
    library(plotly)
    
    ui <- fluidPage(
      plotlyOutput("linePlot")
    )
    
    server <- function(input, output, session) {
      
      initial_plot <- plot_ly(
        x = 0,
        y = 0,
        type = 'scatter',
        mode = 'lines'
      )
      
      output$linePlot <- renderPlotly({
        initial_plot
      })
      
      lineProxy <- plotlyProxy("linePlot", session)
      
      xData <- reactiveVal(0)
      yData <- reactiveVal(NULL)
      
      observe({
        invalidateLater(1000)
        isolate(xData(xData()+1))
        yData(runif(n = 1, min = 0, max = 1))
      })
      
      observe({
        plotlyProxyInvoke(lineProxy, "extendTraces", list(x = list(list(xData())), y = list(list(yData()))), list(0))
      })
      
    }
    
    shinyApp(ui, server)
    

    result

    Also see my related answer here.


    Edit: After the example data was explained.

    Due to the fact, that your data source is updated only once every 24h I'd like to simplify things. The following solution re-renders the chart every minute (we have got the time for that) based on the latest data:

    library(Quandl)
    library(plotly)
    library(shiny)
    
    theme_set(theme_minimal())
    
    ui <- fluidPage(
      plotlyOutput("yield_chart"),
      tags$style(type="text/css", "#yield_chart.recalculating { opacity: 1.0;"), # comment out to see the graph recalculating
    )
    
    server <- function(input, output, session) {
    
      treas_yields <- reactive({
        invalidateLater(1000*60) # update every minute
        DF <- Quandl("USTREASURY/YIELD", api_key="123xyz")
        subset(DF, `10 YR` != 0)
      })
    
      output$yield_chart <- renderPlotly({
        print(paste(Sys.time(), "rendering plotly graph"))
        plot_ly(treas_yields(), x = ~Date, y = ~`10 YR`, name = '10 Yr.', type = 'scatter', mode = 'lines', line=list(color='darkred'))
      })
        
    }
    
    shinyApp(ui, server)