"

40 Storyboards & Dashboards

CONTENT

A cartoon image of a grey Gryphon. It is holding a pointer stick and pointing to something to its right.

  • Storyboarding
  • Dashboards

SLIDE DECKS

Some of the material presented in this chapter will be discussed in the lab. You must ensure you cover all the concepts presented in the lab and in this textbook.

You can also view this chapter by clicking this link:

You can also create the HTML file above by opening this script in R, and then clicking on the Knit (to html_pretty) button.

This lab will be graded. See slides for more information.

Introduction to dashboards

Data has rapidly emerged as one of the most valuable global commodities. We are collecting more data daily than ever before, and the challenge now lies in harnessing its potential. Various sectors, such as commerce, health sciences, and insurance, are turning to dashboards to facilitate data-driven decision-making and create interactive reports and visualizations.

The term “dashboard” is often used loosely, but in a traditional sense, a dashboard is an interactive tool, designed to combine multiple data sources, presenting them through a collection of easily understandable visual elements that convey essential insights. These visual elements include simple charts and graphs that cater to users with diverse data science backgrounds. To draw a parallel, think of a car and its integrated computer, where hundreds of sensors collect data about the vehicle’s various components continually. The driver is only presented with the relevant information, and it is displayed in a simplified and intuitive format. Similarly, digital dashboards serve to simplify complex data from diverse sources into concise, actionable insights.

Dashboards offer several advantages over static reports, some of which include:

Interactivity: Users can tailor the information presented to align with their unique needs or address specific questions using various inputs. For example, when using a COVID-19 dashboard, a person in Guelph, ON may not be as concerned about the prevalence in Vancouver, British Columbia, so they can filter the data in the charts to display only information relevant to their location. For static reports, presenting location-specific information over an entire country requires many pages, with each individual reader needing to sift through to obtain the information they are after.

Real-time Monitoring: In situations with rapidly changing dynamics, static reports can quickly become outdated, sometimes within days or even hours. Creating, analyzing, and publishing data for static reports is time-consuming. In contrast, dashboards can provide information almost instantly, enabling near-real-time monitoring.

Automation: The time of a data scientist is valuable, and performing repetitive tasks, such as analyzing data for monthly reports, is not the most efficient use of their expertise. Dashboards can automate the process of analyzing and publishing information regularly, freeing up data scientists to tackle new and more complex challenges.

Alerts and Notifications: Unlike static reports, dashboards can be configured to issue alerts when specific thresholds are met. To draw a comparison with the car dashboard analogy, most of the data collected is unimportant to the driver as long as it falls within typical parameters. However, when certain data points exceed predefined thresholds, an alert, such as a “check engine” light, signals the need for further investigation. Similarly, dashboards can alert users when specific metrics or conditions breach predetermined limits, ensuring timely attention to critical changes.

Introduction to R Shiny

Shiny is an open-source R package developed by RStudio that allows you to create web applications directly from R code. These applications are accessible through web browsers, making it easy to share data insights and analyses with colleagues, clients, or the general public. With R Shiny, you can transform your static R scripts into dynamic, user-friendly web applications.

R Shiny offers several advantages when compared to alternatives:

  • Full Customization: R Shiny provides a high degree of customization, offering control over the application’s appearance, interactivity, and functionality, allowing you to create tailored, unique data applications to meet your specific needs.
  • R Integration: If you’re already well-versed in R, using R Shiny allows you to seamlessly leverage your existing R skills and libraries, making it an excellent choice for R users looking to create interactive applications from their R code.
  • Open Source: R Shiny is open-source, which means it’s free to use and enjoys a robust and active community of users and developers. You’re not tied to a proprietary platform, and you can modify the source code to suit your requirements.
  • Data Science and Analysis: R Shiny integrates seamlessly with R’s data manipulation and visualization capabilities, making it well-suited for data scientists and analysts who want to create highly customized data-driven applications. Unlike other software for dashboard development, you are not limited to pre-developed chart designs and formats.
  • Reactivity: R Shiny’s reactivity model allows you to build applications that update as users interact with them. This feature makes it a powerful tool for creating interactive dashboards and reports that respond dynamically to user inputs. We will touch more on reactivity in a later section.
  • Machine Learning Integration: Machine learning is quickly gaining interest as computing power and data quantity increase. R has a robust ecosystem for machine learning and statistical modeling, and R Shiny allows you to integrate your machine learning models directly into your applications, enabling you to create predictive and prescriptive analytics dashboards.
  • Community and Resources: R Shiny has an active and supportive user community, ensuring a wealth of resources, packages, and tutorials to assist you in your journey. Almost always, if there is something that you are looking to include in your Shiny app, someone, somewhere has documented a way to do it.

The choice between R Shiny and alternatives like Python Dash, Tableau, or Power BI depends on your specific needs, skills, and project requirements. Each tool has its unique strengths and weaknesses, and your choice should align with your objectives and existing expertise. For instance, Python Dash is a Python-based alternative that offers similar capabilities for Python users, while Tableau and Power BI are known for their user-friendliness and visualization capabilities. However, this document will focus on R Shiny exclusively.

Overview of key concepts

Every R Shiny app consists of two essential components: the User Interface (UI) and the Server, linked together by the concept of reactivity. As their names suggest, the UI shapes the user’s experience and interaction with on-screen content, while the server handles all the computational work in the background. This separation of responsibilities between the UI and Server is a foundational design principle of Shiny. It keeps your code organized and maintainable, particularly as your apps become more complex. Developers can work on the UI and server logic independently, allowing for efficient collaboration and development of Shiny projects.

Reactivity, combined with the UI and Server components, forms the backbone of Shiny’s architecture, making it a versatile and accessible framework for building a wide range of interactive web applications. As we delve further into the topic of reactivity, you’ll discover its full potential and learn how to create dynamic and engaging data-driven apps in R. We will cover the UI, Server and reactivity in more detail in later sections.

For those already familiar with traditional R coding, much of your Shiny app’s server code will resemble the R scripts you’ve been accustomed to creating.

Developing a basic Shiny app

Set-up

Before you can begin developing an interactive dashboard in R Shiny, you must first ensure that you have R and RStudio downloaded on your computer. The Comprehensive R Archive Network (CRAN) is the official source for downloading R and can be accessed at https://cran.r-project.org/. R Studio is an IDE (integrated development environment) developed by Posit, specifically for use with the R programming language. Both programs will need to be downloaded onto your computer but all coding will be conducted through R Studio. Once both programs have been downloaded, go ahead and launch R Studio and use the terminal to install the ‘Shiny’ package.

install.packages(“shiny”)

After installing the Shiny Package, go ahead and create a new Shiny project file by clicking on:

  1. “File”
  2. “New File”
  3. “Shiny Web App…”

You will be prompted to give your project a name, and select if you would prefer to create a “Single File” or “Multiple File” app.

Selecting a “Single file” app will result in one file being generated in your project folder, titled “app.r”. This single “app.R” file will contain both the user interface and serve component in the same code file. Conversely, the “Multiple file” option will generate two files in your project folder; “ui.R” and “server.R”, containing only the UI and server components of your code respectively. For small apps with basic functionality, a “Single file” app may be preferred as you can see all of your code in one place. However, as Shiny apps grow, they can become more and more unwieldy to navigate and a modular system of having your UI and server in separate files may be more practical. Regardless of whether you choose to have a single “app.R” file or a “ui.R” and “server.R” file, the functionality of the app will be identical.

A global file can also be useful for storing code that does not directly fit in to the UI or server, such as loading in libraries, importing data files, or writing functions that you plan to use throughout your code. This file can be added by creating a new R script named “global.R” and storing it in your project folder. Further modularization is possible when developing exceptionally complex Shiny apps, or for those particular about the structure of their code, but this is beyond the scope of this section.

Once you have named your project and selected your application type, the last thing to do is to indicate where you want your project to be saved and click “Create”. A simple shiny app will populate your R Studio workspace. Take a look at how the UI and server components are laid out, and how few lines of code are required to create an interactive histogram where the user can specify the number of bins to plot. You can run this app by clicking “Run App” in the top right of the workspace, which should launch the app in a separate pop-up window.

Basic Structure of a Shiny App

The basic structure of an R Shiny app is as follows:

ui <- fluidPage(
    titlePanel(“My Shiny app”),
    sidebarLayout(
        sidebarPanel(
            # Input elements go here*
        ),
        mainPanel(
            # Output elements go here*
         )
    )
)

server <- function(input, output) {
    # Server logic goes here
}

shinyApp(ui = ui, server = server)

*Not a rule, just common practice. In reality you can put inputs and outputs practically anywhere.

This layout utilizes a sidebarLayout() structure, where typically all of the input elements that the user will interact with are contained within the sidebar, and the outputs (i.e., plots, figures, text) are rendered in the mainPanel. In this code, fluidPage() is used to create the underlying structure. The fluid page layout will conform to the viewport size on whatever machine the user is viewing the app on. Alternatively, a fixedPage() option is available and will present an app with set pixel widths, regardless of the viewport. Shiny offers a variety of layout structures and tools to create virtually endless different app layouts. For a glimpse into what is possible with R Shiny, take a look at some of the example apps (with source code available) on Posit’s website (https://shiny.posit.co/r/gallery/).

Developing the user interface (UI)

The user interface (UI) serves as the essential link between your application and its users, and plays a significant role in impacting their experience. It’s not just about aesthetics; the UI shapes usability and functionality. A well-designed UI should be intuitive, visually appealing, and guide users through the application without the need for much additional instruction. Neglecting its importance can overshadow even the most robust back-end analysis. Therefore, a well-crafted UI should be a fundamental consideration from the outset of your project. Luckily, as mentioned before, Shiny offers a variety of options to tailor your user interface to your audience and the material you are presenting.

To make your Shiny app interactive, the first thing we need to do is provide the user with something to interact with. Shiny offers a catalog of pre-built input types, tailored to accommodate diverse interaction requirements. These basic inputs serve as the cornerstone of user interaction and data manipulation within your application.

Shiny’s basic inputs includes elements like text inputs, select boxes, sliders, action buttons, and radio buttons, among others (see table below). Text inputs allow users to enter text or numeric values, making them suitable for search bars, filters, or data entry forms. Select boxes, on the other hand, facilitate the selection of options from a list, making them useful for dropdown menus and multiple-choice questions. Sliders offer an intuitive way to set numeric ranges, making them valuable for adjusting parameters in data visualization. Action buttons enable users to trigger specific actions or calculations, such as submitting a form or triggering a data update. Radio buttons allow users to choose a single option from a list, making them suitable for exclusive selections like colour or category choices.

Regardless of the chosen input type, there are two shared arguments to configure. The first is the inputId, a reference used exclusively within your code to identify the input, and the second is a label, which provides a description to the user displayed alongside the input element. To access detailed documentation and argument specifics for each input function, you can simply query the terminal using the function ?*Input()(replacing the * with your input of choice). For example:

?selectInput()

Here is an example of an input that we will build on later to create a simple app that allows the user to adjust the frequency of a sine wave.

sliderInput(“frequency”, “Frequency:”, min = 1, max = 10, value = 5)

Now that we have a way for the user to interact with our app, the next thing we need to do is provide them with an output of some sort. For the most part, outputs in a dashboard will be plots and figures. Occasionally, we may also want to present text elements, data tables, or images. However, because the UI component of our Shiny app is focussed only on the design and layout of our app, this is not where we will create our plots, or other outputs, instead we only want to instruct the app on where they will appear for the user to see them. Therefore, dealing with outputs in the UI portion of the code is as simple as telling the app what type of output to expect, and then calling the id (that you define in the server) for your particular output.

In addition to calling the id of your output in the output function, you may also want to specify the size of the output to be displayed. This can be done by defining a height and width within the parenthesis as either percentage of the display window or specific pixel dimensions.

plotOutput(“my_plot”, height = “300px”, width = “500px”)

Combining input and output functions is the backbone of developing the UI of an R Shiny app.

Developing the server

Now that we have established the user interface with defined outputs and interactive inputs, the next step is to add functionality to our application. At this stage, with only the user interface in place, running our Shiny app would present us with all the specified inputs, ready for interaction, but the outputs would remain unpopulated. Although we can interact with the inputs, they lack a purpose in the absence of the back-end logic to interpret them. This is precisely where the server component of Shiny comes into play. The server enables us to define the behavior of our outputs by providing the necessary code for our desired plots and links our inputs to the points where we intend for them to make changes.

Continuing on with our example. First, we will develop the code for a simple plot. Since R Shiny operates in the R universe, we can define our plots just as we would in base R, or using a data visualization package such as library(ggplot2). Assuming that you have an understanding of R programming, we will ignore the particulars of how to create a plot.

output$my_plot <- renderPlot({
    freq <- input$frequency
    x <- seq(0, 2 * pi, length.out = 1000)
    y <- sin(freq * x)

    ggplot(data = data.frame(x = x, y = y), aes(x, y)) +
        geom_line()
        +
labs(title = paste(“Sine Wave (Frequency =”, freq, “)”))
})

The first line of this code is used to assign the function to the my_plot output. Note that this is the same id that we created in the UI in the plotOutput() function, only here it is preceded by output$ since we will be storing it instead of calling it. Also in this first line, we initiate what type of output we are going to create using the renderPlot() function, it is important that this matches the output function in the UI. A mismatch of output types between the server and the UI will not produce the desired output (i.e., plotOutput() in the UI and renderText() in the server). The subsequent three lines of code are dedicated to configuring our sine wave. However, here we introduce an interactive element by allowing the frequency to be controlled by a user-defined input (input$frequency), which we previously established in the UI section of the code. The input$ prefix is used to reference the input element. Following this, the final three lines of code are dedicated to the creation of a fundamental ggplot line plot representing our sine wave. The complete code is encapsulated within parentheses and curly braces, ensuring that this code segment operates within the defined scope.

This simple example demonstrates the basics of linking input and output functions between our UI and server. As you can see, much of the server code is not too dissimilar to standard R coding. All that we did was define our frequency with an input rather than a number, and wrapped all of our code within the renderPlot() function, assigning it to our output. Placing our input and output code blocks within the Shiny framework results in a very basic but functional application.

Reactivity

At the heart of every Shiny app lies the fundamental principle of “reactivity.” This principle is what elevates a Shiny application beyond a static report, transforming it into a dynamic and interactive data tool. In this chapter, we will explore reactivity, a concept that can initially appear complex, particularly to those new to Shiny. However, understanding reactivity is essential for grasping how dynamic applications in R Shiny work.

Reactivity in Shiny can be distilled to: the ability of an application to dynamically respond to user interactions. When users click a button, input data, or make a selection, the application should instantly update and present fresh information without requiring manual page reloading. Reactivity is what enables this. Reactivity can effectively be summarized as follows: user inputs in the interface act as call for change, while the outputs in the interface are the targets of this change, reactive expressions serve as the blueprint for how the change will be achieved. They allow the app to discern user-initiated changes and seamlessly updates any code that relies on these inputs, and repopulate outputs with the updated figures, text, or data tables. While the concept of reactivity is relatively straightforward, its practical implementation can be a challenge. The complexity typically arises from keeping track of the relationships between various inputs, reactive expressions, and outputs in your app. Let’s explore a simple example of implementing reactivity into an app. Here will create an app that populates a plot with random data that can be modified by the user.

First, we load in our shiny library.

library(shiny)

Next, we define a simple user interface, using the sidebar layout. We will include a slider input in the side bar, and two outputs in the main panel; one will be a plot for our randomly generated data, based on the user input, and one will be a text output to reiterate what they have selected with the input.

ui <- fluidPage(
    titlePanel(“Reactivity Example”),
    sidebarLayout(
        sidebarPanel(
            sliderInput(“slider”, “Select a number:”, min = 1, max = 100, value = 50),
            textOutput(“result_text”)
        ),
        mainPanel(
            plotOutput(“plot”)
        )
    )
)

Now we will introduce our reactive expression to link our inputs to the various outputs. We will create a reactive expression called reactive_data, which will create a data frame of random data based on the numer that the user selected with the slider. Notice how all of the code within the reactive function is very similar to standard R code, but we have used inputs in place of static numbers to create our data. Wrapping this code within a reactive expression will result in the data frame, stored as reactive_data, changing any time that any of the inputs within the expression are changed. In this case, we only have one input in the expression, but in an instance where we had two or more, a change to any of the inputs would lead to the expression being re-run.

server <- function(input, output) {
    reactive_data <- reactive({
        data <- data.frame(
            x = 1:input$slider,
            y = rnorm(input$slider)
        )
    data
})

With our reactive expression complete, we can now complete the link between our input and outputs. We have defined our reactive data, and now it can be used just as we would use any other data frame in R, with one caveat. You will notice that behind the reactive_data() is a set of empty parentheses. The reason for using parentheses is that a reactive expression is a function and invoking it with parentheses retrieves the whatever current value it holds. Reactive data is different from static data and therefore needs to be accessed differently.

    output$result_text <- renderText({
        data <- reactive_data()
        paste(“You selected:”, input$slider, “items.”)
    })

    output$plot <- renderPlot({
        data <- reactive_data()
        plot(data$x, data$y, main = “Random Data”)
    })
}

You can now run your R Shiny code by passing your UI and server components to the shinyApp() function.

shinyApp(ui, server)

You might wonder why we didn’t explicitly require a reactive expression when there was only one output. The reason is that Shiny’s render*() functions, like renderPlot() and renderText(), inherently create reactive contexts. They automatically detect dependencies within their code blocks, which simplifies the code and eliminates the need for explicitly defined reactive expressions for simple cases. In more complex apps, or when you need to manage dependencies explicitly, creating custom reactive expressions becomes more necessary. But in simpler scenarios, Shiny’s built-in reactivity handling with render*() functions can streamline your code.

Observe and observeEvent

In addition to the reactive function, there are two other functions that you will want to be aware of, observe and observeEvent. These functions grant you a higher degree of control over reactivity. Unlike reactive expressions, which autonomously monitor dependencies, these functions allow you to stipulate precisely what should trigger their execution.

observe is your go-to choice when you want to supervise an expression and execute specific code blocks in response to changes in its value, offering versatility for handling an array of reactivity scenarios. observe can be thought of as a way to give specific instructions in response to a change in something. You might use it, for instance, to monitor a constantly updated dataset, such as stock prices or sensor readings. When the data crosses a predefined threshold or encounters certain conditions, observe allows you to take instant actions like sending notifications, updating the user interface, or performing other side effects. It’s the mechanism that turns your Shiny app into a real-time, responsive tool for your users, providing a dynamic experience tailored to the changing data.

On the other hand, observeEvent is useful when you need to respond to precise events or changes in input, like an actionButton click or other input widgets. observeEvent can be used to give functionality to a “submit” button, where you want to allow the user the ability to modify several inputs, but you only want the output to be redrawn once they are satisfied with their selections.

Reactivity is the soul of a Shiny app, and without it, there would be virtually no difference between your app and a static report. Though the learning curve may initially seem steep, grasping the principles of reactivity is a milestone and will allow you to create impressive dynamic apps while requiring minimal computational resources.

Deploying your Shiny app

Until now, we’ve developed Shiny apps primarily on our local machines, which suffices for personal use. However, when you intend to share your app with a wider audience, you need to deploy it. Before deploying, the critical first step is choosing the right deployment method. The choice will depend on your specific needs, available resources, and expertise. Common options include Shiny Server, Shinyapps.io, ShinyProxy, and Docker containers, each tailored for different use cases. Shinyapps.io, known for its streamlined process, allows you to publish directly from RStudio using the library(rsconnect) package. If your app involves sensitive data, prioritize security measures like secure connections (HTTPS), access control, and encryption. User authentication can be implemented using packages like ‘shinyauthr’. The choice of deployment method should align with your app’s needs, resources, and security requirements.

Shinyapps.io is perhaps the most streamlined option for deployment as it can be integrated directly with the RStudio IDE and offers a friendly user interface and dashboard for monitoring and managing all of your applications. A free account is required to publish an application to Shinyapps.io. After creating an account, you can retrieve your tokens from the Shinyapps.io dashboard. Simply select a token, and copy the automatically generated rsconnect::setAccountInfo() function directly to your terminal in RStudio to connect your account.

Once you have connected your Shinyapps.io account to your RStudio IDE, you can publish your app by clicking on the blue publish icon to the right of the ‘Run App’ button we have been using to run our apps locally. You can find more detailed information about publish your applications on the Posit website (https://shiny.posit.co/r/articles/share/shinyapps/).

Dashboard best practices

Before you begin coding, it’s essential to take a step back and consider the dashboard’s purpose and the specific needs of your audience. A well-designed dashboard is not just a collection of data visualizations; it’s a powerful tool that provides insights, informs decisions, and simplifies complex information for the end-users. Having a clear direction before you begin will save you time in the long run in terms of revisions, and needlessly coding content that in the end serves no true purpose. Everyone will have a different workflow for the design stage of their applications, be it a pen and paper sketch, or a full-fledged mock-up in a program like Figma. The key is not how you choose to design your app but that you ensure you do it in the first place.

Understand Your Audience: The first step in designing an effective dashboard is to understand your audience. Who will be using the dashboard, and what are their goals and expectations? Tailor your dashboard to cater to the specific needs, preferences, and expertise of the intended users. Consider factors such as their role, technical knowledge, and the key questions they aim to answer through the dashboard. A dashboard designed for the general public to explore COVID-19 data will look much different than a dashboard designed for a group of surgeons looking to explore data on patient recovery in the hospital. When possible, it is best to gather input from a sample of people from your target audience. Identify what information is most important to them and how they want to be able to interact with the data.

Define Clear Objectives: Clearly define the objectives of your dashboard. What insights or actions should users derive from it? Whether it’s monitoring business metrics, analyzing data trends, or making real-time decisions, having well-defined goals will guide your design choices.

Keep it Simple: Simplicity is a fundamental principle of effective dashboard design. Avoid clutter and unnecessary complexity. Present data and visualizations in a straightforward, intuitive manner. Use clear, concise labels and minimize distractions to enable users to focus on the most critical information. Consider the use of tabs or multiple pages to relieve visual clutter and avoid presenting variations of the same plot on a single page. Make use of inputs to allow the user to drive what information is presented to them.

Visual Hierarchy: Create a visual hierarchy that guides the users’ attention. Prioritize the most important information by using prominent visual cues, such as size, colour, or position. Make it easy for users to distinguish between primary and secondary data. In North America, users will typically scan a computer screen in the same pattern as they would read a book, creating a ‘Z’ path with their eyes. Take advantage of this by placing the most important information, or the inputs that you want the user to interact with first in the top-left corner of the content panel. The layout of your dashboard should be user-centric. Organize elements logically, providing a smooth flow from one section to another.

Interactivity: Leverage interactivity wisely. Use interactive elements like filters, drop-down menus, and tooltips to allow users to explore the data further. However, strike a balance to prevent overwhelming the dashboard with unnecessary interactive features. Several packages exist for creating interactive plots and tables with clean and intuitive user experiences in mind. Some notable packages are:

  • library(Plotly): for interactive graphs and plots.
  • library(Leaflet): for interactive maps
  • library(DT): for interactive data tables.

Data Integrity: Ensure the accuracy and integrity of the data presented. Data quality is crucial, and any inconsistencies or inaccuracies can erode trust in the dashboard’s information.

Mobile Responsiveness: Recognize that users may access the dashboard from various devices. Design your dashboard with responsiveness in mind to ensure it functions well on desktops, tablets, and mobile devices. Shiny can manage some of this automatically through bootstrapping, but manual intervention may be required.

Feedback and Iteration: Software development is never over, and revisions will always be necessary to keep your app current and running smoothly. Collect feedback from potential users or stakeholders during the design phase. Iterate on the dashboard’s design based on their input. Continuous improvement is key to creating a dashboard that truly serves its audience.

General design considerations: Pay attention to design details like color palette, typography, and overall aesthetics. Select a color scheme that aligns with your branding or conveys the right emotions (there is a lot of research on the psychology of colours). If you struggle with design, take advantage of the countless free online tools for creating/finding a colour palette. Opt for a sans-serif font for readability and to avoid appearing outdated. A pleasant and visually appealing app encourages user engagement, helps in conveying information more effectively, and instills confidence in the user. Adobe Color is a free tool that allows you to create your own palette or choose from a library of existing palettes (https://color.adobe.com/create/color-wheel).

 

License

Community Engaged Data Science Copyright © 2023 by Daniel Gillis. All Rights Reserved.