How to animate plotly plot in shiny when data changes SOLVED!

I figured out how to get a R-shiny-plotly plot to smoothly transition when the data changes. I didn't want the action button approach provided by R-plotly. I was trying to get smooth animations as per the linked example via plotlyProxyInvoke("animate", ...):

The trick is passing the right list structure. Also bar traces can't currently be animated in plotly, so I had to fake it using fat lines. Enjoy!

library(shiny)
library(plotly)
library(dplyr)

ui <- fluidPage(
	actionButton("go", "Click to recalc"),
	plotlyOutput("plot")
)

gendata <- function(){
	ndata <- 10
	d <- tibble(text=LETTERS[1:ndata], f=1, x=runif(ndata)) %>% mutate(r = rank(x))
	rbind(mutate(d, x=-1), d, mutate(d, x=-1)) %>%
		arrange(text)
}

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

	origdata <- gendata()
	if (FALSE){ # for offline testing
		print(head(origdata))
		my <- list(olddata = origdata, newdata = origdata)
	}

	my <- reactiveValues(
		olddata = origdata,
		newdata = origdata
		)

	output$plot <- renderPlotly({
		cat("renderPlotly\n")
		plot_ly() %>%
			add_trace(x=origdata$x, y=origdata$r, frame=origdata$f, line=list(width=20, simplify=FALSE), type="scatter", opacity=0.5, mode="lines", name="Rank") %>%
			add_trace(x=origdata$x + 0.02, y=origdata$r, frame=origdata$f, text=origdata$text, type="scatter", mode="text", showlegend=FALSE) %>%
			layout(xaxis=list(range=list(0,1.1))) %>%
			animation_opts(frame=500, transition=500, redraw=FALSE)
	})

	observeEvent(input$go, {
		req(my$newdata)
		cat("observeEvent input$go\n")
		my$olddata <- my$newdata # save old data
		my$newdata <- gendata() %>% # generate new data
			mutate(f=my$olddata$f+1)
		print(head(my$newdata))
		# https://plot.ly/javascript/plotlyjs-function-reference/#plotlyanimate
		plotlyProxy("plot", session=session, deferUntilFlush=FALSE) %>%
			plotlyProxyInvoke("animate",
							  # frameOrGroupNameOrFrameList
							  list(
							  	data = list(list(
											  	x = my$newdata$x,
											  	y = my$newdata$r,
											  	frame = my$newdata$f
								  	        ),
							  				list(
								  			 	x = my$newdata$x + 0.02,
								  			 	y = my$newdata$r,
								  			 	text = my$newdata$text,
								  			 	frame = my$newdata$f
							  			 )),
							  	traces = list(as.integer(0), as.integer(1)),
							  	layout = list()
							  ),
  						  	  # animationAttributes
		                      list()
							  )# plotlyProxyInvoke
	})

}

shinyApp(ui, server)
4 Likes

Do you think this is scalable? I would like to add similar animations, but am having performance issues as it is (using several filters for my data).

My data is quite large (>6 Mio. observations) and I'm really looking to improve performance. Will using plotly proxy help? I don't have a lot of experience using plotly.

My guess is it will only help a little. You will need to be smart about you handle your data. Some things you can do:

  1. Preprocess your data as much as possible outside your app and save it as a small, fast format (e.g. RDS or feather).
  2. Try to avoid making copies of your data inside your app. From what I understand, R won't make copies of your data unless it has to (it mainly just passes around pointers in a smart way). Even if you filter it just creates an index I think.
  3. Using data.table might be faster than dplyr in some cases.
  4. You can use profvis to find out which lines are slow.
  5. Vectorise everything you can.
  6. Maybe use an aesthetic that summarises the data (e.g. heatmap) instead of plotting millions of raw points.
  7. Make sure you are not plotting lots of copies of the same thing - this can happen for example when you calculate a mean using dplyr and then plot the mean. You actually only need one copy.
  8. Filter stuff that appears outside the plot window. I'm not sure how plotly handles it, it might actually ignore data outside the plot window anyway. I had to plot non-visible rows of my data thought to get smooth animations.
  9. etc

Thanks for the detailed response. Because I use a bunch of filters, there's not a lot of room for preprocessing the data. I will definitely check out other formats. Currently I'm importing the data from a .csv file.
I'm already using data.table and have tried to check my code with profvis, but at the end I just get an error message - I haven't had time to check this out in detail. I'll try to improve my code using your suggestions! Thank you!

If you're using data.table, I believe fread is super fast.

Yes, I used fread before trying read_rds. There's no noticable difference.

This topic was automatically closed 54 days after the last reply. New replies are no longer allowed.