Short introduction to interactive visualisation in R

Author

Laurent Gatto

This very short introduction to interactive visualisation in R will teach you:

More specifically, for the shiny application, you will learn about the two components of an application, namely the user interface and server, and how the reactive programming paradigm enables communication between them.

Interactive plots with plotly

Install the package with

install.packages("plotly")

The plotly package can be used for creating interactive web graphics open source JavaScript graphing library plotly.js and its R binding.

Once a ggplot2 figure is stored in a variable such as p below …

library("ggplot2")
data(iris)
p <- ggplot(data = iris,
            aes(x = Sepal.Length, y = Sepal.Width, colour = Species)) +
    geom_point()

… the variable can simply be pass to the ggploty() function:

library("plotly")
ggplotly(p)

See the plotly for R book for more details and to learn how to build plotly plots from scratch in R.

Interactive apps with shiny

Install the package with

install.packages("shiny")

Quoting the shiny package desciption:

Makes it incredibly easy to build interactive web applications with R.

As opposed to plotly above, when using shiny, one tends to aim for more complete, long-lasting applications, rather than transient visualisations.

A shiny application is composed of a ui (user interface) and a server that exchange information using a programming paradigm called reactive programming: changes performed by the user to the ui trigger a reaction by the server and the output is updated accordingly.

  • In the ui: define the components of the user interface (such as page layout, page title, input options and outputs), i.e what the user will see and interact with.

  • In the server: defines the computations in the R backend.

  • The reactive programming is implemented through reactive functions, which are functions that are only called when their respective inputs are changed.

  • An application is run with the shiny::runApp() function, that takes the directory containing the ui and server as input.

Let’s build a simple example from scratch, step by step. This app, shown below, uses the faithful data, describing the wainting time between eruptions and the duration of the reuption for the Old Faithful geyser in Yellowstone National Park, Wyoming, USA.

head(faithful)
  eruptions waiting
1     3.600      79
2     1.800      54
3     3.333      74
4     2.283      62
5     4.533      85
6     2.883      55

It shows the distribution of waiting times along a histogram (produced by the hist function) and provides a slider to adjust the number of bins (the breaks argument to hist).

The app can also be opened at https://lgatto.shinyapps.io/shiny-app1.

Creation of our fist shiny app

  1. Create a directory that will contain the app, such as for example "shinyapp".
  2. In this directory, create the ui and server files, named ui.R and server.R.
  3. In the ui.R file, let’s defines a simple (fluid) page containing
    • a title panel with a page title;
    • a layout containing a sidebar and a main panel
shinyUI(fluidPage(
    titlePanel("My Shiny App"),
    sidebarLayout(
        sidebarPanel(
        ),
        mainPanel(
        )
    )
))
  1. In the server.R file, we define the shinyServer function that handles the inputs (coming from the ui) and ouputs (returned back to the ui) … there are none at this stage, and the R logic.
shinyServer(function(input, output) {
})
  1. Let’s now add some items to the ui: a text input widget in the sidebar and a field to hold the text ouput.
shinyUI(fluidPage(
    titlePanel("My Shiny App"),
    sidebarLayout(
        sidebarPanel(
            textInput("textInput", "Enter text here:")
        ),
        mainPanel(
            textOutput("textOutput")
        )
    )
))
  1. We can now populate the shinyServer function in the server.R file. Below, we add some R code defining how to manipulate the user-provided text and render it using a shiny textOuput.
shinyServer(function(input, output) {
    output$textOutput <- renderText(paste("User-entered text: ",
                                          input$textInput))
})
  1. Let’s now add a plot in the main panel in ui.R and some code to draw a histogram in server.R:
shinyUI(fluidPage(
    titlePanel("My Shiny App"),
    sidebarLayout(
        sidebarPanel(
            textInput("textInput", "Enter text here:")
        ),
        mainPanel(
            textOutput("textOutput"),
            plotOutput("distPlot")
        )
    )
))
shinyServer(function(input, output) {
    output$textOutput <- renderText(paste("User-entered text: ",
                                          input$textInput))
    output$distPlot <- renderPlot({
        x    <- faithful[, 2]
        hist(x)
    })

})
  1. We want to be able to control the number of breaks used to plot the histograms. We first add a sliderInput to the ui for the user to specify the number of bins, and then make use of that new input to parametrise the histogram.
shinyUI(fluidPage(
    titlePanel("My Shiny App"),
    sidebarLayout(
        sidebarPanel(
            textInput("textInput", "Enter text here:"),
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
        ),
        mainPanel(
            textOutput("textOutput"),
            plotOutput("distPlot")
        )
    )
))
shinyServer(function(input, output) {
    output$textOutput <- renderText(paste("User-entered text: ",
                                          input$textInput))
    output$distPlot <- renderPlot({
        x    <- faithful[, 2]
        bins <- seq(min(x), max(x), length.out = input$bins + 1)
        hist(x, breaks = bins)
    })

})
  1. The next addition is to add a menu for the user to choose a set of predefined colours (that would be a selectInput) in the ui.R file and use that new input to parametrise the colour of the histogramme in the server.R file.
shinyUI(fluidPage(
    titlePanel("My Shiny App"),
    sidebarLayout(
        sidebarPanel(
            textInput("textInput", "Enter text here:"),
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30),
            selectInput("col", "Select a colour:",
                        choices = c("steelblue", "darkgray", "orange"))
        ),
        mainPanel(
            textOutput("textOutput"),
            plotOutput("distPlot")
        )
    )
))
shinyServer(function(input, output) {
    output$textOutput <- renderText(paste("User-entered text: ",
                                          input$textInput))
    output$distPlot <- renderPlot({
        x    <- faithful[, 2]
        bins <- seq(min(x), max(x), length.out = input$bins + 1)
        hist(x, breaks = bins, col = input$col)
    })

})
  1. The last addition that we want is to visualise the actual data in the main panel. We add a dataTableOutput in ui.R and generate that table in server.R using a renderDataTable rendering function.
## Define UI for application that draws a histogram
shinyUI(fluidPage(

    ## Application title
    titlePanel("My Shiny App"),

    ## Sidebar with text, slide bar and menu selection inputs
    sidebarLayout(
        sidebarPanel(
            textInput("textInput", "Enter text here:"),
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30),
            selectInput("col", "Select a colour:",
                        choices = c("steelblue", "darkgray", "orange"))
        ),

        ## Main panel showing user-entered text, a reactive plot and a
        ## dynamic table
        mainPanel(
            textOutput("textOutput"),
            plotOutput("distPlot"),
            dataTableOutput("dataTable")
        )
    )
))
## Define server logic
shinyServer(function(input, output) {

    output$textOutput <- renderText(paste("User-entered text: ",
                                          input$textInput))

    ## Expression that generates a histogram. The expression is
    ## wrapped in a call to renderPlot to indicate that:
    ##
    ##  1) It is "reactive" and therefore should be automatically
    ##     re-executed when inputs change
    ##  2) Its output type is a plot
    output$distPlot <- renderPlot({
        x    <- faithful[, 2]  ## Old Faithful Geyser data
        bins <- seq(min(x), max(x), length.out = input$bins + 1)

        ## draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = input$col, border = 'white')
    })

    output$dataTable <- renderDataTable(faithful)

})

Single-file app

Instead of defining the ui and server in their respective files, they can be combined into list

ui <- fluidPage(...)
server <- function(input, output) { ... }

To be run as

shinyApp(ui = ui, server = server)

With RStudio

With RStudio, one can create an RStudion shiny project to automate the creationg of a directory with a single-file shiny application.

Sharing shiny apps

  • Share the code file(s) and runApp.
  • runUrl will download and launch a Shiny application that is hosted at a downloadable URL.
  • runGitHub to run an application available on GitHub 1.
  • shinyapps hosts Shiny application.
  • Shiny server (in-house)

For more details about shiny, visit the main shiny website or consult the Mastering Shiny book.

Session information

The source of this document is available at https://github.com/lgatto/interactive-vis-intro. This document was generated on Fri Jul 7 08:34:18 2023.

sessionInfo()
R version 4.3.0 (2023-04-21)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 22.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/atlas/libblas.so.3.10.3 
LAPACK: /usr/lib/x86_64-linux-gnu/atlas/liblapack.so.3.10.3;  LAPACK version 3.10.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_GB.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_GB.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_GB.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Brussels
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] plotly_4.10.2 ggplot2_3.4.2

loaded via a namespace (and not attached):
 [1] gtable_0.3.3      jsonlite_1.8.7    highr_0.10        dplyr_1.1.2      
 [5] compiler_4.3.0    tidyselect_1.2.0  tidyr_1.3.0       scales_1.2.1     
 [9] yaml_2.3.7        fastmap_1.1.1     R6_2.5.1          labeling_0.4.2   
[13] generics_0.1.3    knitr_1.43        htmlwidgets_1.6.2 tibble_3.2.1     
[17] munsell_0.5.0     pillar_1.9.0      rlang_1.1.1       utf8_1.2.3       
[21] xfun_0.39         lazyeval_0.2.2    viridisLite_0.4.2 cli_3.6.1.9000   
[25] withr_2.5.0       magrittr_2.0.3    crosstalk_1.2.0   digest_0.6.32    
[29] grid_4.3.0        rstudioapi_0.14   lifecycle_1.0.3   vctrs_0.6.3      
[33] evaluate_0.21     glue_1.6.2        data.table_1.14.8 farver_2.1.1     
[37] fansi_1.0.4       colorspace_2.1-0  rmarkdown_2.23    purrr_1.0.1      
[41] httr_1.4.6        ellipsis_0.3.2    tools_4.3.0       pkgconfig_2.0.3  
[45] htmltools_0.5.5  

Footnotes

  1. You can for instance run this app with runGitHub("interactive-vis-intro", "lgatto", subdir = "/src/shiny-app1")↩︎