Embedding Plotly Charts into a Shiny App

Last week I attended a couple of data science talks at the DataPhilly meetup. One of the presenters was Matt Sundquist from Plotly. He gave an overview of some of the capabilities and features of Plotly. During the talk, Matt mentioned that Plotly visualizations can be embedded into a Shiny application. Shiny is a web application framework available in RStudio which allows you to create web applications using only R.

A while back I made a Shiny app as an exercise to learn about the framework using the heart disease data set in the UCI repository. The app uses K-nearest neighbors to classify the presence or absence of heart disease given several features. I decided to alter the app using Plotly graphs in place of the ggplot2 graphs in the original app. You can try both apps out and get the code at the links below. The graphs can be found by clicking the Visualize Features tab in the app. (NOTE: The apps are embedded below for convenience and may appear somewhat compressed. For the best visability you may want to view the app in a separate window by clicking the provided links)

Overview of the Shiny App Layout

The apps contains 4 tabs:

  • Classifier - This tab contains slider bar for selection of k and check boxes for features to be included during the classification. A confusion matrix and the classification error value for the selected parameters is displayed.
  • Visualize Features - This tab allows the user to view the distribution of two features and the two features plotted against one another. The color in the plots is based on the presence (positive) or absense (negative) of heart disease in the dataset.
  • Feature Description - Provides a short description of each feature
  • References - Data source references.

Embedding Plotly in the App

The plots present in the Visualize Features tab were originally created using the ggplot2 package. To implement Plotly graphs in place of the old graphs a few new functions were necessary. Lucky for me Plotly provides some examples in GitHub which I used as my starting point.

To provide the new functionality to my app I used the two files listed below:

  • plotlyGraphWidget.R
  • plotlyGraphWidget.js

Both files can be found in the ggplot2 UN example. I copied both the files into my Shiny app project folder. (NOTE: For the plotlyGraphWidget.js file I copied the complete www directory.)

The plotlyGraphWidget.R script contains two functions: graphOutput, which will be used to display the plot in the ui.R file, and renderGraph, which is used in the server.R file. The original app uses the plotOutput function in the ui.R file to define where a plot is shown.

fluidRow(
          column(4, plotOutput("distPlotA")),                              
          column(4, plotOutput("distPlotB")),
          column(4, plotOutput("ScatterPlot"))
        )

To use Plotly, the graphOutput function is swapped in place of the plotOutput function as shown below.

fluidRow(
          column(4, graphOutput("distPlotA")),                              
          column(4, graphOutput("distPlotB")),
          column(4, graphOutput("ScatterPlot"))
        )

After updating the ui.R file, the server.R file needs to be updated. The first step is to replace the instances of renderPlot with renderGraph. In the base app a ggplot object was created inside the renderPlot function, but to use Plotly the ggplot object must be converted to a list containing the plot details. This task is handled by the gg2list function provided in the plotly library.

 ggscatter <- ggplot(ds, aes_string(x = input$featureDisplay_x, 
                          y = input$featureDisplay_y, 
                          color = "factor(num)")) + 
      geom_point(size = 8, position = position_jitter(w = 0.1, h = 0.1)) + 
      labs(x = input$featureDisplay_x,
           y = input$featureDisplay_y) +
      fte_theme() + 
      scale_color_manual(name = "Heart Disease",values=c("#7A99AC", "#E4002B")) 
  
      # convert plot details to list
      # for plotly
      fig <- gg2list(ggscatter)

From this point the data and layout information for the Plotly graph can be edited then returned by the renderGraph function.

data <- list()
for(i in 1:(length(fig)-1)){data[[i]]<-fig[[i]]}

layout <- fig$kwargs$layout
# Remove the existing annotations (the legend label)        
layout$annotations <- NULL 

# place legend to the right of the plot
layout$legend$x <- 100
layout$legend$y <- 1
list(
   list(
        id="ScatterPlot",
        task="newPlot",
        data=data,
        layout=layout
      )
    )