Time Series Visualisation with gghighlight and Shiny - Part 2
In the previous post, I showed how the gghighlight package can allow the showing of many time series in the background whilst focusing on a handful of selected series. Can this idea be extended with interactivity?
ggplot2
shiny
Author
Josh Cowley
Published
October 27, 2022
Tl;dr
Tl;dr
Updating a plot by click events on the same plot is not as straightforward as one would imagine and we must save clicks in a buffer.
For the advanced Shiny user, see the app at app-current/.
Basic Setup
Recall we can simulate data from the simulate_correlated_normal function, this method is made available again but is only called once at the start of the server function.
We make a slight change to the visualisation method since the data wrangling done at the start of function was causing some issues with Shiny’s nearpoints method.
app-current/ggplot_ts.R
#' Visualise Time-Series Data#' #' Plot used in blog post to show many time-series like data.#' #' @param plot_data .#' @param highlight character vector. Names of variables to highlight.ggplot_ts <-function(plot_data, highlight =NULL) { out <- plot_data %>% ggplot2::ggplot(ggplot2::aes(x = rowid,y = value,colour = name )) + ggplot2::geom_line(alpha =0.8) + ggplot2::geom_point(alpha =0.8) + ggplot2::labs(x ="Time", y ="Value", colour ="Series") + ggplot2::guides(colour ="none")if (is.null(highlight)) return(out) out + gghighlight::gghighlight( .data$name %in% highlight,unhighlighted_params = ggplot2::aes(alpha =0.2),use_group_by =FALSE )}
App 1 - Naive Attempt
The goal is simply to render a plot using the ggplot_ts function and update one of its arguments when the plot is clicked on.
To achieve this on a static plot in Shiny is straightforward, supply an ID in the UI section of the app:
shiny::plotOutput("plot", click ="plot_click")
And then the click information is available via input$plot_click in the server side of the app. It typically looks like this.
But, instead of working with this directly we can use the following to get a subset of the plot data (allRows = FALSE) or the entire data with a convenient column named selected_ (allRows = TRUE).
This works as we store the click information in a buffer accessible by save_events$plot_click.
saved_events <-reactiveValues(plot_click =NULL)
This is then updated by an observer within bindEvent. You can think of the syntax bindEvent(observe({ x }), ...) as run { x } whenever one of the events in ... is triggered.
By using this buffer the reflection where a plot update will update click information is removed and only a click will update this buffer.
gghighlight
Issue
Back to the example at hand, extra care has to be given with gghighlight since it modifies the underlying plot object.
In the current iteration of the app, we start with some time series data,
This (or some other difference between p1 and p2) means that any subsequent clicks will be looking for highlight..........1 within the original data which clearly doesn’t exist and the app will crash.
Solution
The solution to this is simple (after hours of finding trial and error!). We pass the x and y names explicitly in the nearPoints call:
The server file loads the required functions, simulates two datasets and then implements what has been shown in this blog post within the server function.
Only one of the two plots change the series when clicked since nearPoints needs to know which dataset it is working on. The extension to 2 is straightforward but we would ideally have this work for any number of plots.
Shiny modules could allow this extension by using namespaces. Also modules also allow for functionality to be
isolated for testing,
re-used multiple times in the same app,
more easily shared.
Image Credit
Josh Cowley. October 25th, 2022. “Quayside Stone Sculptures, Newcastle Upon Tyne”.