Reporting Tools

 Theory Input

We are using the bike data again, but please use the following updated dataset:

Data

I. RMarkdown

R Markdown provides an authoring framework for data science. You can use a single R Markdown file to both

  • save and execute code
  • generate high quality reports that can be shared with an audience

R Markdown documents are fully reproducible and support dozens of static and dynamic output formats. You are using them already to build your journal. This 1-minute video provides a quick tour of what’s possible with R Markdown:


The cheat sheet for Rmarkdown can be reached in RStudio via: File > Help > Cheatsheets > R Markdown Cheat Sheet

I have provided a complete .Rmd file with the most important information for you. You need to download the zip file called reporting_rmarkdown.zip. The file contains a .Rmd file and an image folder with an image.

After placing it there, open the .Rmd file by clicking it. If you want, open the outline by clicking the outline button in the upper right corner. In the following we are going through each of the containing pieces.


Very important: If you want to create PDFs you need to install LaTex. The easiest way to install is with the tinytext package:

install.packages("tinytex")
tinytex::install_tinytex()

Now click the play button in line 35 to load the necessary libraries. Then go to line 139-141 and update the path. You should get an output like this:

Now you should be able to create a HTML file by clicking on the arrow next to the knit button and by clicking on Knit to HTML. If you click Knit to PDF you should get an error message:

Error: Functions that produce HTML output found in document targeting latex output.

This is because PDF will only work with static plots like the one from ggplot2. To resolve that error you have to comment out line 202. ggplotly() converts a ggplot into an interactive plot (will be discussed in the next section).


Let’s go through the .Rmd file and see how it’s broken up and see what options are available to you.

YAML

The upper part (the header) between the dashes is the YAML section, that controls your document properties. There is the information like title, author and date and some output information. If you create a rmd document (New File -> R Markdown), it may not look like this with all of these options put in here. I have reformatted these outputs based on what I want to produce. Right now it is set up to produce a pdf document (whatever is first in the outputs is going to be produced). Many of those options are listed on the R Markdown cheat sheet on page 2, depending on the output:

  • code_folding: Can be set to show, hide, none. Know your audience: This option should be set depening on whether you send the document to technical audiences (e.g. data scientists) or to non-technical audiences (e.g. business leaders).
  • df_print: default, kable, tibble, paged. For large datasets creating a pageable table might be a good idea.
  • highlight: Specifies the syntax highlighting style. Supported styles include default, tango, pygments, kate, monochrome, espresso, zenburn, haddock, breezedark, and textmate. Pass null to prevent syntax highlighting.
  • number_sections: Add section numbering to headers
  • theme: Specifies the Bootstrap theme to use for the page (themes are drawn from the Bootswatch theme library). Valid themes include default, cerulean, journal, flatly, darkly, readable, spacelab, united, cosmo, lumen, paper, sandstone, simplex, and yeti. Pass null for no theme (in this case you can use the css parameter to add your own styles).
  • toc: Add a table of contents (TOC)
  • toc_depth: Specify the depth of headers that it applies to
Special Code Chunk sets our ‘Global Options’

The second part is what we call a code chunk. This is one is a special code chunk because it is the set up. All of your rmarkdown documents run this part first to set up the socalled chunk options. This is just another way to control your document. Global options can be changed inside knitr::opts_chunk$set(), Local options can be changed inside each code chunk (you can see that in the second code chunk: {r, echo=FALSE}).

  • echo: Whether to echo (show) the source code in the output document (someone may not prefer reading your smart source code but only results). Tip: Use echo = FALSE globally when sending reports to Non-technical people
  • eval: Whether to evaluate a code chunk. Toggles whether or not to run the code.
  • results: When set to ‘hide’, text output will be hidden; when set to ‘asis’, text output is written “as-is”. Toggles whether or not to show output (text, tables, plots, etc.).

There are many more options you can control than we have not covered here. You can get them by running knitr::opts_chunk$get() in the console.

Information about how to format text, insert headers, text, lists and images can be found in the provided .Rmd file.

You can organize content using tabs by applying the .tabset class attribute to headers within a document (HTML). This will cause all sub-headers of the header with the .tabset attribute to appear within tabs rather than as standalone sections. Tabsets are great for building interactive reports with multiple plots. For example:

## Quarterly Results {.tabset}

### By Product

(tab content)

### By Region

(tab content)

You can also specify two additional attributes to control the appearance and behavior of the tabs. The .tabset-fade attribute causes the tabs to fade in and out when switching between tabs. The .tabset-pills attribute causes the visual appearance of the tabs to be “pill” rather than traditional tabs. For example:

## Quarterly Results {.tabset .tabset-fade .tabset-pills}

II. Plotly

Let’s switch gears and move on to Plotly. Plotly’s R graphing library takes ggplot-plots a step further and converts them into dynamic, interactive and publication-quality graphs. Examples are line plots, scatter plots, area charts, bar charts, error bars, box plots, histograms, heatmaps, subplots, multiple-axes, and 3D (WebGL based) charts. It enables us to really investigate the data much easier than with a static plot.

In this section we are showing you how to create a custom interactive plotting function, that can adjust aggregation (sales trend: weekly, monthly, quarterly). It will be powered by plotly, that turns static ggplots into dynamic plots.

1. Preparing data for plotting

Let’s start by loading and joining the data. This time we are not using the stock information but some made up sales data (orderlines):

# INTERACTIVE PLOTS ----

# GOAL: DEVELOP INTERACTIVE PLOTS FOR A SALES REPORT

# LIBRARIES & DATA ----

# Main
library(tidyverse)
library(lubridate)

# Visualization
library(plotly)

bikes_tbl      <- readRDS("bikes_tbl.rds")
bikeshops_tbl  <- readRDS("bikeshops_tbl.rds")
orderlines_tbl <- readRDS("orderlines_tbl.rds")

bike_orderlines_tbl <- orderlines_tbl %>%
    left_join(bikes_tbl,     by = c("product_id" = "bike_id")) %>%
    left_join(bikeshops_tbl, by = c("customer_id" = "bikeshop_id")) %>%
    
    # Add the total price
    mutate(total_price = price_euro * quantity)

Let’s create two functions for the EURO formatting. We can use the scales::dollar and scales::dollar_format functions, but we have to adjust them to the euro format:

format_to_euro <- function(x, suffix = " €") {

  scales::dollar(x,
                 suffix       = suffix,
                 prefix       = "",
                 big.mark     = ".",
                 decimal.mark = ",")
}

euro_format <- function(scale        = 1,
                        prefix       = "",
                        suffix       = " €",
                        big.mark     = ".",
                        decimal.mark = ",") {

  scales::dollar_format(suffix       = suffix,
                        prefix       = prefix,
                        big.mark     = big.mark,
                        decimal.mark = decimal.mark,
                        scale        = scale)

}

After loading the data, we can bring it in the right format for plotting with ggplot. We only need the date and the total_price column. With those we can summarize the data to visualize the total sales by year, month, week etc. To do that we create a new feature date_rounded with the function floor_date() to round down the dates to month. To round them up, we can use ceiling_date().

For each datapoint we create a label, that contains the sales number in a currency format and the date in a readable way. This is going to be the hover text in the interactive plot. We can do that using the str_glue function. Remember, that everything in curved brackets is R code while concatenating.

# 1.0 TOTAL SALES BY MONTH ----

# 1.1 Preparing Time Series Data ----
# Monthly

total_sales_m_tbl <- bike_orderlines_tbl %>%

  select(order_date, total_price) %>%

  mutate(date_rounded = floor_date(order_date, unit = "month")) %>%

  group_by(date_rounded) %>%
  summarise(total_sales = sum(total_price)) %>%
  ungroup() %>%

  mutate(label_text = str_glue("Sales: {format_to_euro(total_sales)}
                                 Date: {date_rounded %>% format('%B %Y')}"))

total_sales_m_tbl

To explain the format() function go to devhints.io/strftime. strftime stands for string format times and is a very common way to express dates. You can think of it like RegEx.

Try it out (as_datetime() converts character string to date-time format):

?format 
# Scroll down to Details and go to to date-times (click format.POSIXct)
# All Abbreviations are listed here as well

"2011-01-01 00:00:00" %>% as_datetime() %>% format("%B %Y")
## [1] "January 2011"

Above we are using single quotes, because the entire expression is already wrapped into double quotes.

2. Create a static plot

Now that we have the date formatted in the way appropriate for our interactive plotting, we can create the code for the plot. The first step is the same as always. You will see, interactive plots are really easy if you know how to create ggplots.

# 1.2 Interactive Plot ----

# Step 1: Create ggplot with text feature

g1 <- total_sales_m_tbl %>%
  ggplot(aes(x = date_rounded, y = total_sales)) +

  # Geoms
  geom_point() +
  geom_smooth(method = "loess", span = 0.2) +

  # Formatting
  
  # Convert scale to euro format
  scale_y_continuous(labels = euro_format()) +
  
  # Make sure 0 will always be shown (even if the data is far away)
  expand_limits(y = 0) +
  
  labs(
    title = "Total Sales",
    y = "Revenue (EUR)",
    x = ""
  )

g1

3. Create a dynamic plot

Make the static plot dynamic is pretty easy. Just put the plot into ggplotly() and hit enter. You will see as you hover over each of the points it’s got the date_rounded and the total_sales information.

# Step 2: Use ggplotly()
ggplotly(g1)

The only issue is that the hover text is not showing the way that we want. We want to show the label text, which we have created earlier. Setting up the hover text is easy. Just set aes(text = label_text) in ggplot then set tooltip = “text” in ggplotly. In short, we have to replace the geom_point() line with geom_point(aes(text = label_text)). You will get a warning (Ignoring unknown aesthetics: text). You can ignore that.

ggplotly(g1, tooltip = "text")

To make it more flexible, let’s wrap it into a custom time series function:

# 1.3 Plot Total Sales Function ----

plot_total_sales <- function(unit = "month", date_format = "%B %Y", interactive = TRUE) {

  # Handle Data
  data_tbl <- bike_orderlines_tbl %>%

    select(order_date, total_price) %>%

    mutate(date_rounded = floor_date(order_date, unit = unit)) %>%

    group_by(date_rounded) %>%
    summarise(total_sales = sum(total_price)) %>%
    ungroup() %>%

    mutate(label_text = str_glue("Sales: {format_to_euro(total_sales)}
                                 Date: {date_rounded %>% format(date_format)}"))

  # Make Plot
  g1 <- data_tbl %>%
    ggplot(aes(x = date_rounded, y = total_sales)) +

    # Geoms
    geom_point(aes(text = label_text), color = "#2C3E50") +
    geom_smooth(method = "loess", span = 0.2) +

    # Formatting
    scale_y_continuous(labels = euro_format()) +
    expand_limits(y = 0) +
    labs(
      title = "Total Sales",
      y = "Revenue (Euro)",
      x = ""
    )

  # Static vs Interactive Logic
  if (interactive) {
    return(ggplotly(g1, tooltip = "text"))
  } else {
    return(g1)
  }

}
# 1.4 Test Our Function ----

plot_total_sales(unit = "weekly", date_format = "%B %d, %Y", interactive = TRUE)

plot_total_sales(unit = "monthly", date_format = "%B %Y", interactive = TRUE)

Exercise

Reproduce this function, but this time make a faceted plot, that visualizes the sales by category 2

Step 1: Prepare the data. Next to order_date and total_price, we need category_1 and category_2. Watch out how to group the data.

Hint 1.1 (Factoring the category_2 column)

For the faceted plot, the category_2 column has to be factored and reordered by date_rounded first and then total_sales second.

mutate(category_2 = as_factor(category_2) %>%
           fct_reorder2(date_rounded, total_sales))

Hint 1.2 (Complete data preparation):

# 2.0 CATEGORY 2 SALES BY MONTH ----
# 2.1 Preparing Time Series Data ----
category_2_sales_m_tbl <- bike_orderlines_tbl %>% select(order_date, category_1, category_2, total_price) %>% mutate(date_rounded = floor_date(order_date, unit = "month")) %>%
group_by(date_rounded, category_1, category_2) %>% summarise(total_sales = sum(total_price)) %>% ungroup() %>%
mutate(label_text = str_glue("Sales: {format_to_euro(total_sales)} Date: {date_rounded %>% format('%B %Y')}")) %>%
mutate(category_2 = as_factor(category_2) %>% fct_reorder2(date_rounded, total_sales))

Step 2: Make a faceted plot and use ggplotly().

Hint 2:

# 2.2 Interactive Plot ----
# Step 1: Create ggplot g2 <- category_2_sales_m_tbl %>% ggplot(aes(x = date_rounded, y = total_sales, color = category_2)) +
# Geoms geom_point(aes(text = label_text)) + geom_smooth(method = "loess", span = 0.2) + facet_wrap(~ category_2, scales = "free_y", ncol = 3) +
# Formatting expand_limits(y = 0) + theme(legend.position = "none", # Change the height so the text looks less squished strip.text.x = element_text(margin = margin(5, 5, 5, 5, unit = "pt"))) + scale_y_continuous(labels = euro_format(scale = 1e-3, suffix = "K €")) + labs( title = "Sales By Category 2", y = "", x = "" )
# Step 2: Use ggplotly() ggplotly(g2, tooltip = "text")

Step 3: Make it a function, which is really flexible in terms of inputs. You can use these default arguments as a starting point:

plot_categories <- function(category_1 = "All", category_2 = "All",
                            unit = "month", date_format = "%B %Y",
                            ncol = 1, scales = "free_y",
                            interactive = TRUE) { }
                            
# Examples of running the function (Use | as an OR operator)
plot_categories(category_1 = "All", 
                category_2 = "(Gravel|Cyclo|Fat)", 
                unit = "month",
                ncol = 1, 
                scales = "free_y", 
                date_format = "%Y-%m-%d")

plot_categories(category_1 = "All", 
                category_2 = "Endurance", 
                unit = "day",
                ncol = 1, 
                scales = "free_y", 
                date_format = "%Y-%m-%d")

plot_categories(category_1 = "(Gravel|Mountain)", 
                category_2 = "All", 
                unit = "quarter",
                ncol = 2, 
                scales = "free_y", 
                date_format = "%Y-%m-%d")

Hint 3.1: Handling inputs for category_1 and category_2

# Handle Inputs
  cat_1_text <- str_to_lower(category_1)
  cat_2_text <- str_to_lower(category_2)
# Create Filter Logic if (cat_1_text != "all") { data_tbl <- data_tbl %>% filter(category_1 %>% str_to_lower() %>% str_detect(pattern = cat_1_text)) }
if (cat_2_text != "all") { data_tbl <- data_tbl %>% filter(category_2 %>% str_to_lower() %>% str_detect(pattern = cat_2_text)) }

Hint 3.2: Complete Function

# 2.3 Plot Categories Function ----
plot_categories <- function(category_1 = "All", category_2 = "All", unit = "month", date_format = "%B %Y", ncol = 1, scales = "free_y", interactive = TRUE) {
# Handle Data
data_tbl <- bike_orderlines_tbl %>% select(order_date, category_1, category_2, total_price) %>% mutate(date_rounded = floor_date(order_date, unit = unit)) %>%
group_by(date_rounded, category_1, category_2) %>% summarise(total_sales = sum(total_price)) %>% ungroup() %>%
mutate(label_text = str_glue("Sales: {format_to_euro(total_sales)} Date: {date_rounded %>% format(date_format)}")) %>%
mutate(category_2 = as_factor(category_2) %>% fct_reorder2(date_rounded, total_sales))
# Handle Inputs cat_1_text <- str_to_lower(category_1) cat_2_text <- str_to_lower(category_2)
# Create Filter Logic if (cat_1_text != "all") { data_tbl <- data_tbl %>% filter(category_1 %>% str_to_lower() %>% str_detect(pattern = cat_1_text)) }
if (cat_2_text != "all") { data_tbl <- data_tbl %>% filter(category_2 %>% str_to_lower() %>% str_detect(pattern = cat_2_text)) }
# Make Plot g2 <- data_tbl %>% ggplot(aes(x = date_rounded, y = total_sales, color = category_2)) +
# Geoms geom_point(aes(text = label_text), color = "#2c3e50") + geom_smooth(method = "loess", span = 0.2) + facet_wrap(~ category_2, scales = scales, ncol = ncol) +
# Formatting expand_limits(y = 0) + theme(legend.position = "none", strip.text.x = element_text(margin = margin(5, 5, 5, 5, unit = "pt"))) + scale_y_continuous(labels = euro_format(scale = 1e-3, suffix = "K €")) + labs( title = "Sales By Category 2", y = "", x = "" )
# Static Vs Interactive Logic if (interactive) { return(ggplotly(g2, tooltip = "text")) } else { return(g2) }
}

A way to save your functions:

# 3.0 SAVE FUNCTIONS ----
# 3.1 Create a file
fs::file_create("00_scripts/plot_sales.R")

# 3.2 Save funtions to the file
dump(list = c("plot_total_sales", "plot_categories", "format_to_euro", "euro_format"), file = "00_scripts/plot_sales.R")

III. Flexdashboard

Dashboards are particularly common in business-style reports. They can be used to highlight brief and key summaries of a report. The layout of a dashboard is often grid-based, with components arranged in boxes of various sizes.

With the flexdashboard package, you can

  • Use R Markdown to publish a group of related data visualizations as a dashboard.
  • Embed a wide variety of components including HTML widgets, R graphics, tabular data, gauges, value boxes, and text annotations.
  • Specify row or column-based layouts (components are intelligently re-sized to fill the browser and adapted for display on mobile devices).
  • Create story boards for presenting sequences of visualizations and related commentary.
  • Optionally use Shiny to drive visualizations dynamically.

Everything you need to know can be found here:

Learning Objectives

  1. Gain familiarity with flexdashoboard
  2. Make a chloropleth map using plotly (Chloropleth map: Maps that cover geographic boundaries)

Before you start you have to install the package:

install.packages("flexdashboard")

We are going to create a basic flexdashboard app for our sales analysis. To author a dashboard, you can create a document from within RStudio using the File -> New File -> R Markdown dialog, and choosing a “Flex Dashboard” template.

Make sure to save the template, that you have just openend. You can hit the knit button to see what happens. It will just open the basic framework for flexdashboard. You can also pop it out to view it in you browser. Flexdashboard is just a layout / container for an app. A way of organizing the information we want to display. That layout is built using RMarkdown. User inputs are added with Shiny (will talk about it down the road).

The basic template comes with 3 Charts. You can add as many as you want (by adding columns and charts) and control their sizing with the orientation argument in the header and the data-height / data-width values in the columns. Orientation can be set to either rows or columns. If you set vertical_layout: fill, the data-heights are applied proportionally. If it is set to scroll, the exact numbers of pixels are applied. Charts can be stacked by adding .tabset to the curved brackets after Column (e.g. {data-heigth=650 .tabset}). .tabset .tabset-fade does work as well. Using multiple pages is another way of organizing the data in your dashboard.

Just check out the following example to see which commands control which options. This is a template for a multi-page app structure with Side Bar.

Let’s start to create an one page app, that is going to be a sales dashboard. At this point we will be using only flexdashboard together wit plotly. But eventually we will be integrating shiny and other technologies on top of it, that will really build a functioning app.

Step 1: Initialize the layout

Initiliaze a new Rmarkdown file with the following header:

---
title: "Sales Dashboard"
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: fill
---

Step 2: Load the libraries

Next to the mandatory flexdashboard package and the known packages tidyverse and plotly, we need raster and sf to be able to plot polygons. The goal is to visualize the states of Germany to show which states generate the biggest revenue.

Additionally, we need to load the function format_to_euro() to display the values in a euro format. We can do that by sourcing the R script, which we have saved earlier.

```{r setup, include=FALSE}
library(flexdashboard)

# Core
library(tidyverse)

# Interactive Visualizations
library(plotly)

# Spatial Data
library(raster)
library(sf)

# Currency formatting
source("00_scripts/plot_sales.R")
```

Step 3: Code preparation

You can knit the document already at this point, but because we didn’t put anything into it, you will only see the canvas with the title. Let’s create some input. Load and join the bike data (Joins can take a long time with large data, and therefore should not be performed inside your Web App. It would be better to have a database backend and perform the joins there. But for now, we can do it inside the App). Watch out to adjust your pathes. After that we are loading and transforming the spatial data for Germany.

```{r}
# Bike data
bikes_tbl      <- readRDS("01_data/bikes_tbl.rds")
bikeshops_tbl  <- readRDS("01_data/bikeshops_tbl.rds")
orderlines_tbl <- readRDS("01_data/orderlines_tbl.rds")

bike_orderlines_tbl <- orderlines_tbl %>%
    left_join(bikes_tbl,     by = c("product_id" = "bike_id")) %>%
    left_join(bikeshops_tbl, by = c("customer_id" = "bikeshop_id")) %>%
    mutate(total_price = price_euro * quantity)

# German spatial data
germany_sp <- getData('GADM', country='DE', level=1) 
# Convert SpatialPolygonsDataFrame to an sf dataframe
germany_sf <- st_as_sf(germany_sp) %>% 
                  # Add english names
                  mutate(VARNAME_1 = ifelse(is.na(VARNAME_1), NAME_1, VARNAME_1)) 
```

Step 4: Wrangle and plot data in a new section

Take a look at the germany_sf object. It has already all the information to plot the map. But we still need the revenue information aggregated per state so that we can see the information as we hover over a state. We just need to group and summarize the bike data, join it with the geographic data and create labels for the hover texts. The wrangled data can then be plotted with the plot_ly() function. In the arguments of plot_ly() we use the ~ to specify which column to use. Plotly can use Brewer color palettes for the color schemes (e.g. “Blues”).

Column {data-width=1000}
---------------------------------------------------------------

### By State
```{r}
geo_plot_tbl <- bike_orderlines_tbl %>% 
                  group_by(state) %>%
                  summarise(total_revenue = sum(total_price)) %>%
                  ungroup() %>%
                  right_join(germany_sf, by = c("state" = "VARNAME_1")) %>% 
                  mutate(total_revenue = ifelse(is.na(total_revenue), 0, total_revenue)) %>% 
                  mutate(label_text = str_glue("State: {state}
                                         Revenue: {format_to_euro(total_revenue)}")) %>% 
                  # Convert back to an sf object, that can be plotted
                  st_as_sf()
```

```{r}
plot_ly(geo_plot_tbl, 
        split      = ~NAME_1, 
        color      = ~total_revenue,
        colors     = "Blues",
        stroke     = I("black"),
        hoverinfo  = 'text', 
        text       = ~label_text, 
        hoveron    = "fills", 
        showlegend = FALSE) 
```

The final script:

In the following section we’ll see how we can integrate further functionalities and reactivity to our dashboard using shiny.

IV. Shiny

Shiny is an R package that makes it easy to build interactive web apps straight from R. You can host standalone apps on a webpage or embed them in R Markdown documents or build dashboards. You can also extend your Shiny apps with CSS themes, htmlwidgets, and JavaScript actions.

A Shiny app is mainly two components:

  • a user interface component (UI) defining the front end of the app. It’s responsible of displaying components on the web page. Components may be inputs (slider, dropdown menus, buttons, check boxes, text, …) or outputs (graphs, tables, text, …). Interactions with input components are detected and may modify outputs via the server component.
  • a server component playing the role of the back end of the app. It’s responsible of preparing data and generating graphs reflecting user interactions.

Resources

Let’s start with a shiny tutorial. To get a first impression download the following file:

The first thing to mention is: there is an additional piece of code in the YAML: runtime: shiny. This enables using the shiny server to power your flexdashboard app (as opposed to rmarkdown rendering as HTML). That enables viewers to change underlying parameters and see the results immediately.

---
title: "Shiny Tutorial"
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: fill
runtime: shiny
---

If you want you can hit the Run Document Button. It will show you how the document will look like. We will fill the empty boxes in the next steps. Note that RMarkdown tab is “spinning”. This is because Shiny is running as a server process. Click the stop button if you want to stop it.

The steps required to add Shiny components to a flexdashboard are as follows:

  1. Add runtime: shiny to the options declared at the top of the document (YAML front matter).
  2. Add the {.sidebar} attribute to the first column of the dashboard to make it a host for Shiny input controls (note this step isn’t strictly required, but many Shiny based dashboards will want to do this).
  3. Add Shiny inputs and outputs as appropriate.
  4. When including plots, be sure to wrap them in a call to renderPlot(). This is important not only for dynamically responding to changes but also to ensure that they are automatically re-sized when their container changes.

Before we start, let’s create some random data (code is already present in the document):

```{r}
# Make data
set.seed(123)
sales_data_tbl <- tibble(
  date  = seq.Date(from = ymd("2018-01-01"), to = today(), length.out = 90),
  class = sample(c("marketing", "sales", "research"), size = 90, replace = TRUE),
  sales = runif(n = 90, min = 1, max = 100)
)
```

1. Checkbox Input

To create a Checkbox group we are using the function checkboxGroupInput(). It creates a UI (User Interface) Checkbox Component that users can toggle on an off for multiple choices.

The Shiny package makes available a wide variety of functions for creating inputs, a few of them include:

R FunctionInput Type
selectInputA box with choices to select from
sliderInputA slider bar
radioButtonsA set of radio buttons
textInputA field to enter text
numericInputA field to enter numbers
checkboxInputA single check box
dateInputA calendar to aid date selection
dateRangeInputA pair of calendars for selecting a date range
fileInputA file upload control wizard

In our case for the checkboxGroupInput, we need the arguments

  • inputId: A unique ID that we use to refer to the input typically as intput$inputId.
  • label: Display label for the control
  • choices: List of values to show checkboxes for. If elements of the list are named then that name rather than the value is displayed to the user.
  • selected: The values that should be initially selected, if any.

h4() converts text into an h4 header.

We want the checkbox to filter down on the class column of our generated data above (the unique choices are marketing, research and sales). Place the following code below the first column, that includes the {.sidebar} attribute:

```{r}
# Inputs
checkboxGroupInput(inputId = "checkbox_group", 
                   label   = h4("Checkbox Group"), 
                   choices = c("Marketing" = "marketing",
                               "R&D"       = "research",
                               "Sales"     = "sales"), 
                   selected = c("marketing", "research", "sales"))
```

That has generated the UI component. Next we need to do something with that component. When you use Shiny within a flexdashboard you’ll be making use of both input elements (e.g. sliders, checkboxes, etc.) and output elements (plots, tables, etc.). Input elements are typically presented within the sidebar and outputs within flexdashboard content panes (it’s also possible to combine inputs and outputs in a single pane).

Outputs react to changes in input by running their render code and displaying updated output. The Shiny package also includes a wide variety of render functions, including:

R FunctionOutput Type
renderPlotR graphics output
renderPrintR printed output
renderTableData frame, matrix, other table like structures
renderTextCharacter vectors

Let’s output the selection in the pane Checkbox Output. To do that, we have to fill out the code chunk and link it to our checkbox group. To output the selection, we can use renderPrint(). It renders the printed output generated from a reactive expression.

input is a special object (list) that keeps track of all UI components. input allows us to refer to them in render functions using the format: input$inputId.

The checkboxGroupInput call makes a checkbox input named “checkbox_group” available. The renderPrint() function is then able to access the values of the “checkbox_group” input via the expression input$checkbox_group.

Column {data-width=500}
-----------------------

### Section - Checkbox Output

```{r}
renderPrint(input$checkbox_group)
```

If you run the document now, you should be able to see the output. We don’t only want to see the output. We want to return the output, to be able to do something with it. Typically for a checkbox input is filtering some data.

output is a special object (list) that keeps track of all rendered objects. output allows us to refer to them in Output functions using the format: output$Id

Let’s assign the rendered values in the output and give them the Id checkbox_values. This is done via the $ sign. After that we can do stuff with it. E.g. printing it again with textOutput().

Column {data-width=500}
-----------------------

### Section - Checkbox Output

```{r}
output$checkbox_values <- renderPrint(input$checkbox_group)

textOutput(outputId = "checkbox_values")
```

2. Date range Input

Ok, now let’s try a different type of input. A data range input. We can put the code in the input section and use the following arguments:

  • inputId: Id to acces the value.
  • label: Display label for the control.
  • start: The initial start date.
  • end: The initial end date.
  • min: The minimum allowed date, the user can input. We can just apply the minum date in our data.
  • max: The maximum allowed date, the user can input. We can just apply the maximum date in our data.
  • startview: The date range shown when the input object is first clicked. Can be “month” (the default), “year”, or “decade”.
dateRangeInput(inputId = "date_range", 
               label   = h4("Date Range"), 
               start   = "2018-01-01", 
               end     = today(),        
               min     = min(sales_data_tbl$date), 
               max     = max(sales_data_tbl$date), 
               startview = "year")

The output is very similar to last time.

### Section - Date Range

```{r}
output$date_range_values <- renderPrint(input$date_range)

textOutput(outputId = "date_range_values")
```

3. Slider Input

For the next input below the date range input we are using sliderInput(). This generates a UI component for a Slider or Slider Range, with which we can filter or sales values.

sliderInput(inputId = "slider_input", 
            label   = h4("Slider Range"), 
            min     = 1,
            max     = 100, 
            value   = c(40, 60), # Initial values of the slider
            step    = 1, # Interval between each selectable value
            round   = TRUE, # Round to the nearest integer
            pre     = "$")

For the slider output we follow the same pattern that we have done before:

### Section - Slider Output

```{r}
output$slider_values <- renderPrint(input$slider_input)

textOutput(outputId = "slider_values")
```

4. Reactive Output

Now you have a good understanding of the reactivity from just utilizing the inputs. But what we next need to learn is how to actually use these inputs to modify our data in some way. We want to be able to apply a reactive output that filters our data.

  • reactive() turns a normal expression (e.g. modifying data with the tidyverse) into a reactive expression (one that changes over time as the user uses the input). Use brackets {} to tell R that the function argument will need to go on multiple lines.
  • filter() filters row-wise by providing an expression that can be evaluated as TRUE/FALSE. %in% determines which values are in a vector of values, returning TRUE/FALSE if the value is in or not in the vector of values.
  • between() is used to return a logical value if the data is between to values. Great for filtering by a date range.
  • ymd() transforms dates stored in character and numeric vectors to Date objects
Column {data-width=750}
-----------------------

```{r}
# Reactive Filter
sales_data_filtered <- reactive({
  
  sales_data_tbl %>%
    filter(class %in% input$checkbox_group) %>%
    filter(date %>% between(left  = ymd(input$date_range[1]), 
                            right = ymd(input$date_range[2]))) %>%
    filter(sales %>% between(left  = input$slider_input[1],
                             right = input$slider_input[2]))
})

```

We can use renderPrint(), renderTable() (renders a dataframe or tibble as a basic HTML table) or renderDataTable() (renders a dataframe or tibble as a JavaScript Data Table) from the DT package to display the reactive output. Reactive Outputs must have parenthesis. This is a major source of errors, forgetting to use parenthesis (e.g. sales_data_filtered()).

We could just add DT::renderDataTable(expr = sales_data_filtered() ) to our code and we would get an output. But we are not able to scroll down in the app. To add a scrollbar, we have to modify the options argument of the function. Check ?renderDataTable or rstudio.github.io/DT/options.html for further information. We can also modify the output with a mutate function, because it is basically just a dataframe. Let’s format the numbers to dollar values in this step.

Even though it’s not always necessary, it’s a good idea to get in the habbit of saving rendered output as output$outputId. DTOutput() outputs a rendered data table using it’s unique outputId.

### Section - Reactive Output

```{r}
#  renderPrint( sales_data_filtered() )

output$dt_output_1 <- DT::renderDataTable(expr = {
  
  sales_data_filtered() %>%
    mutate(sales = scales::dollar(sales))
  
}, options = list(scrollY = "250px"))

DTOutput(outputId = "dt_output_1")
```

Try for yourself to add another DT output to the Section “Reactive Summary”. It should show the summarized sales for each department.

### Section - Reactive Summary
```{r} output$dt_output_2 <- DT::renderDataTable(expr = {
sales_data_filtered() %>% group_by(class) %>% summarise(total_revenue = sum(sales)) %>% ungroup() %>% mutate(total_revenue = scales::dollar(total_revenue))
}, options = list(scrollY = "250px"))
DTOutput(outputId = "dt_output_2") ```

5. Reset Button

Here we show you how to create a reset button to toggle back all these user inputs to the defaults. We can create a button that when pressed does something by using the function actionButton(). We just need to give it an inputId and a label. We can also include an icon with the icon() function. The function connects to the Font Awesome Library and Glyphicons Library. Use ?icon to check how you can see a lists of available icons.

```{r}
# Reset Button
actionButton(inputId = "reset", 
             label   = "Reset", 
             icon    = icon("sync"))
```

To trigger a reaction when the button is pressed, we use the function observeEvent(). It responds to “events” (e.g. Button Clicks). The response goes to the handlerExpr argument and the inputId to the eventExpr argument.

Every UI Input has an updateInput() function (e.g. sliderInput() has updateSliderInput()). With those functions we can set the input to the default values. The session argument is always session, the inputId argument corresponds to the Id of the object, which we are changing, and the others change the values. You can put the following code underneath the actionButton.

observeEvent(eventExpr = input$reset, handlerExpr = {
  
  updateCheckboxGroupInput(session  = session, 
                           inputId  = "checkbox_group", 
                           selected = c("marketing", "research", "sales"))
  
  updateDateRangeInput(session = session, 
                       inputId = "date_range", 
                       start   = "2018-01-01", 
                       end     = today())
  
  updateSliderInput(session = session, 
                    inputId = "slider_input", 
                    value   = c(40, 60))
  
  
})

That’s it. Now you are ready for your last challenge!

 Challenge

RMarkdown

Create a Rmd document, that can generate a pdf and a html output. The generated pdf report should look like this …

… and the html report like this:

Shiny

Take the flexdashboard, which we have created in this session (with the German map), make it reactive and add shiny components to it. It does not necessarily has to look like the following, but it should have a selection function for the Bike Type (category_1) and for the Bike Family (category_2) and a date range slider. For the bike selection tools, I have used shinyWidgets::checkboxGroupButtons(), shinyWidgets::pickerInput(). Check dreamrs.github.io/shinyWidgets/index.html to see the code. Using the shinyWidgets package is optional, you can also use the built in checkboxes. Use renderPlotly() and plotlyOutput() to plot the map reactively. Don’t forget to put runtime: shiny at the top of the Rmarkdown file.

The other functions, that I have used were: shinyWidgets::radioGroupButtons() and renderValueBox().

Previous
Next