Interactive plots - advanced

This article contains information about using Shiny’s image and plot interaction features to perform some more advanced tasks.
Published

May 30, 2017

This article contains information about using Shiny’s image and plot interaction features to perform some more advanced tasks.

To get a look at most of the features available in plot interactions, see the advanced demo app.

Interactions with bitmap images

The plot interaction article describes how to interact with plots generated by R’s base graphics and ggplot2. Shiny also supports interactions with arbitrary bitmap (for example, PNG or JPEG) images. There is one change in the information returned for these mouse events: instead of plot coordinates scaled to the data, they will contain pixel coordinates. You may need to transform these coordinates to something useful for your data.

The only difference in the code is that, instead of using renderPlot(), you would use renderImage(). For an example, see the image interaction demo app.

Mouse event data

If you’d like to see the data structures returned by mouse interactions, see the basic demo app.

Zooming

Mouse interactions can be used to implement zooming in plots. The zooming demo app shows two ways of doing this: by zooming in a single plot, and by using one plot to control the zoom in a second plot.

Excluding points from a scatter plot

It can be useful to interactively select outliers to exclude from a prediction model. The exclude demo app shows how to do this.

Dates and date-times

When dates and date-times are used on the x or y axis, the selected values will be returned from the browser as numeric values. The nearPoints() and brushedPoints() functions will automatically handle the type conversions, but if you want to do the conversions manually, you would use something like the following:

# If the x variable is a Date
as.Date(input$plot_click$x, origin = "1970-01-01")

# If the y variable is POSIXct
as.POSIXct(input$plot_click$y, origin = "1970-01-01")

The origin is the date or time to count from, and midnight on 1970-01-01 is the usual value.

Note: for datetimes, it is generally preferable to use data of class POSIXct instead of POSIXlt, because the storage format of POSIXlt is more difficult to work with.

Another possibility is, instead of converting the mouse coordinates to dates or times, you could convert the data values to numbers, and then do some comparison with the input values:

# If the x variable, in data$dates, is a Date
# Find which rows are within 1 day of the click
selectedRows <- abs(as.numeric(data$dates) - input$plot_click$x) < 1

Categorical axes (including bar graphs)

For plots that have axes with categorical values (factors or character vectors), the values returned from the browser will be numeric. To compare the mouse coordinate values to the data values, you will need to coerce the data to numeric values.

For mouse click/double-click/hover events, you will typically want to round the mouse’s x or y value so that it can be compared to the data values. The app below demonstrates how to do this:

library(shiny)
library(ggplot2)

ui <- fluidPage(
  fluidRow(
    column(6,
      plotOutput("plot1", click = "plot1_click")
      ),
    column(5,
      br(), br(), br(),
      htmlOutput("x_value"),
      verbatimTextOutput("selected_rows")
    ))
)

server <- function(input, output) {
  output$plot1 <- renderPlot({
    plot(ToothGrowth$supp, ToothGrowth$len)
  })
  
  # Print the name of the x value
  output$x_value <- renderText({
    if (is.null(input$plot1_click$x)) return("")
    else {
      lvls <- levels(ToothGrowth$supp)
      name <- lvls[round(input$plot1_click$x)]
      HTML("You've selected <code>", name, "</code>",
           "<br><br>Here are the first 10 rows that ",
           "match that category:")
    }
  })
  
  # Print the rows of the data frame which match the x value
  output$selected_rows <- renderPrint({
    if (is.null(input$plot1_click$x)) return()
    else {
      keeprows <- round(input$plot1_click$x) == as.numeric(ToothGrowth$supp)
      head(ToothGrowth[keeprows, ], 10)
    }
  })
}

shinyApp(ui, server)

Click on each box on the plot below to see this in action:

For brushing, it usually make more sense to check if a factor level’s corresponding numeric value is within the xmin and xmax (or ymin and ymax).

Learn more

For more on this topic, see the following resources:

Interactive Graphics with Shiny

Creating interactive web graphics suitable for exploratory data analysis