Skip to content

Interaction Design for Data Exploration

Visualizations capable of launching detail views can add value to a data analyst’s user experience. Programming in this kind of interaction automates the creation of complementary charts and increases ease of exploration by linking varied views of the data in a logical way.

This tutorial offers a quick example of how to create a click input function with Shiny.onInputChange  in JavaScript. The visualization used for this example is a heatmap drawn with Highcharts, since the hc_add_event_point  function in the R library highcharter  does not apply for clicks on heatmaps as of this writing. A similar approach can likewise be applied to any visualization drawn with highcharter , whenever the data retrieved and sent from a JavaScript object to R requires customization.

library(shiny)
library(highcharter)
library(ggplot2)

red_wine <- read.csv('winequality-red.csv', sep = ';')

Sample Visualization: Correlation Matrix

Exploring relationships among variables in a dataset is an important first step in getting to know and prepping data for machine learning.

Whenever a cell is clicked on the correlation matrix below, the graph will yield a scatter plot of the two variables corresponding to that specific correlation value.

The Wine Quality dataset from the University of California, Irvine data archive for machine learning datasets was used. The data can be downloaded here.

Sending the Data from JavaScript to Shiny

The chart above requires the following R libraries:

library(shiny)
library(highcharter)
library(ggplot2)

We read in the data as red_wine :

red_wine <- read.csv('winequality-red.csv', sep = ';')

From there, generating the heatmap with highcharter  is simple. In the code for our simple Shiny app below, we apply the cor  function to the data, then pass the resulting matrix to hchart .

We add the JavaScript function that extracts the x and y data series names from the correlation matrix inside hc_plotOptions  as a click event. The event  parameter is a matrix cell click, a JavaScript object we can deconstruct. Using this object that we have named event , we create the xstr  and ystr  variables.

From there, we can build the data structure to be returned: an array called data  with the variables x , y , and nonce . The nonce  variable, simply a random number, is commonly used in click interactions: its purpose is to allow for two consecutive clicks on the same element. Otherwise, the event  data will be the same as before, and JavaScript will not interpret this event  as a change, remaining unresponsive until the user clicks on a different element.

We call the JavaScript function Shiny.onInputChange  to send a custom message from JavaScript to Shiny. The first parameter matrix_click  is the unique name for the event which Shiny observers and reactive functions can listen for. We can now refer to the event as input$matrix_click  elsewhere in our code, as we would for other Shiny inputs. The second parameter, data , is the custom message to be sent from JavaScript to R after every click event.

shinyApp(
  ui <- fluidPage(
   
   # Application title
   titlePanel("Red Wine Quality")
   
   , fluidRow(column(8
                     , highchartOutput('wine_corr', height = "600px")
                     )
              )
   ),
  
  server <- function(input, output, session) {
  
  # remove `quality` rating
  red_wine <- subset(red_wine, select = -quality)
  red_wine_corr <- cor(red_wine)
  
  output$wine_corr <- renderHighchart({
    hchart(red_wine_corr) %>%
      hc_plotOptions(
        series = list(
          boderWidth = 1,
          dataLabels = list(enabled = FALSE),
          events = list(
            click = JS("function(event) {
                      
                         var xstr = event.point.series.xAxis.categories[event.point.x].toString();
                         var ystr = event.point.series.yAxis.categories[event.point.y].toString();
  
                         var data = {
                            x: xstr,
                            y: ystr,
                            nonce: Math.random()
                          };
  
                         Shiny.onInputChange('matrix_click', data);

                       }"))
          )
        )
  })
  
  #  event listener
  observeEvent(input$matrix_click, {
    
    # retrieve series from red_wine using x and y variables from click data
    red_wine$x_val <- red_wine[[input$matrix_click$x]]
    red_wine$y_val <- red_wine[[input$matrix_click$y]]
    
    # code for ggplot scatter plot
    output$scatterPlot <- renderPlot({
      ggplot(red_wine, aes(x=x_val, y=y_val)) + geom_point() +
        ylab(input$matrix_click$y) + xlab(input$matrix_click$x)
    })
    
    # overlay with ggplot scatter plot 
    showModal(
      modalDialog(
        fluidRow(
          column(12,
                 h3(paste0(input$matrix_click$y, " ~ ", input$matrix_click$x))
          )
        )
        , plotOutput("scatterPlot")
        , size = "l"
        , easyClose = TRUE
      )
    )
  })
   
},

options = list(
    width = "100%", height = 750
  )

)

We can now capture the variable names for any given value in the correlation matrix as a click input. Using these variable names to access the original data series, Shiny can spin up scatter plots on the fly. Not only can we compare correlation values by looking at the cell colors of the correlation matrix, we can also zero in on the shape of variable relationships of interest. A different, more detailed view of the data is just a click away.