Creating interactive apps

In the previous section, we demonstrated the use of R Markdown that is designed for creating dynamic documents. In this section, we will take a quick tour of creating interactive apps where we use a graphical user interface to interact with the data.

Creating a shiny app

R itself is a great environment for data analysis and visualization. However, it is not usual to deliver R and some analytic scripts to the customers to run by themselves. The outcome of data analysis can be presented not only in a HTML page, PDF document, or a Word document, but also in an interactive app that allows readers to interact with the data by modifying some parameters and see what happens with the outcome.

A powerful package, shiny (http://shiny.rstudio.com/), developed by RStudio, is designed exactly for this purpose. A shiny app is different from the interactive graphics we demonstrated previously. It works in a web browser and the developer has all the say about what appears in the web page and how users can interact with it. To achieve this, a shiny app basically consists of two important parts: An HTTP server that interacts with the web browser, and an R session that interacts with the HTTP server.

The following is a minimal shiny app. We write an R script to define its user interface (ui) and server logic. The user interface is a boostrapPage which contains a numericInput to take an integer of sample size and a textOutput to show the mean of the randomly generated sample. The logic behind server is to simply generate random numbers according to the sample size (n) in the input and put the mean of the random sample to the output:

library(shiny) 
 
ui <- bootstrapPage( 
  numericInput("n", label = "Sample size", value = 10, min = 10, max = 100), 
  textOutput("mean") 
) 
 
server <- function(input, output) { 
  output$mean <- renderText(mean(rnorm(input$n))) 
} 
 
app <- shinyApp(ui, server) 
runApp(app) 

The definition is now complete and we can source the code in RStudio to play with this minimal shiny app, as shown in the following screenshot:

Creating a shiny app

Each time we change the number of the sample size, the HTTP server will ask the R backend to rerun the server logic and refresh the output mean.

Although the preceding example is not useful, it at least demonstrates the basic components of a shiny app. Now we look at a more complicated but useful example.

The following example is a visualizer of many paths generated by geometric Brownian motion which is often used to model stock prices. As we know, a geometric Brownian motion is characterized by starting value, expected growth rate (r), volatility (sigma), duration (T) and the number of periods. Expect for T = 1, we allow users to modify all other parameters.

Now we can define the user interface of the shiny app according to the parameters we want to expose to users. The shiny package provides a rich set of input controls listed as follows:

shiny_vars <- ls(getNamespace("shiny")) 
shiny_vars[grep("Input$", shiny_vars)] 
##  [1] "checkboxGroupInput"       "checkboxInput"            
##  [3] "dateInput"                "dateRangeInput"           
##  [5] "fileInput"                "numericInput"             
##  [7] "passwordInput"            "selectInput"              
##  [9] "selectizeInput"           "sliderInput"              
## [11] "textInput"                "updateCheckboxGroupInput" 
## [13] "updateCheckboxInput"      "updateDateInput"          
## [15] "updateDateRangeInput"     "updateNumericInput"       
## [17] "updateSelectInput"        "updateSelectizeInput"     
## [19] "updateSliderInput"        "updateTextInput" 

To control the randomness of the generated paths, we allow users to specify the random seed (seed) so that the same seed produces the same paths. In the following code where ui is defined, we use numericInput for seed and sliderInput for other parameters. The sliderInput control has a certain range and step so that we can force a parameter to take reasonable values.

The user interface not only defines the input part but also the output part, that is, where to show what. The following is all output types shiny provides:

shiny_vars[grep("Output$", shiny_vars)] 
## [1] "dataTableOutput"    "htmlOutput"         
## [3] "imageOutput"        "plotOutput"         
## [5] "tableOutput"        "textOutput"         
## [7] "uiOutput"           "verbatimTextOutput" 

In this example, the shiny app only shows a plot of all paths put together to indicate different possibilities with the same set of parameters:

library(shiny) 
ui <- fluidPage( 
  titlePanel("Random walk"), 
  sidebarLayout( 
    sidebarPanel( 
      numericInput("seed", "Random seed", 123), 
      sliderInput("paths", "Paths", 1, 100, 1), 
      sliderInput("start", "Starting value", 1, 10, 1, 1), 
      sliderInput("r", "Expected return", -0.1, 0.1, 0, 0.001), 
      sliderInput("sigma", "Sigma", 0.001, 1, 0.01, 0.001), 
      sliderInput("periods", "Periods", 10, 1000, 200, 10)), 
  mainPanel( 
    plotOutput("plot", width = "100%", height = "600px") 
  )) 
) 

Once the user interface is defined, we need to implement the server logic which is basically about generating random paths according to user-specified parameters and put them together in the same plot.

The following code is a simple implementation of the server logic. First we set the random seed. Then we iteratively call sde::GBM to generate random paths from geometric Brownian motion. To install the package, run install.packages("sde") before calling GBM:

The GBM package is responsible for generating one path while sapply is used to combine all generated paths into a matrix (mat) where each column represents a path. Finally, we use matplot to plot each path in different colors together in one chart.

The calculation is done in render* functions no matter whether it is a text, image, or a table. The following lists all the render functions shiny provides:

shiny_vars[grep("^render", shiny_vars)] 
## [1] "renderDataTable" "renderImage"     "renderPage"      
## [4] "renderPlot"      "renderPrint"     "renderReactLog"  
## [7] "renderTable"     "renderText"      "renderUI" 

In this example, we only need renderPlot() and to put the plotting code in it. The output$plot function will go to plotOutput("plot") in the user interface when the input is modified:

server <- function(input, output) { 
  output$plot <- renderPlot({ 
    set.seed(input$seed) 
    mat <- sapply(seq_len(input$paths), function(i) { 
      sde::GBM(input$start,  
        input$r, input$sigma, 1, input$periods) 
    }) 
    matplot(mat, type = "l", lty = 1, 
      main = "Geometric Brownian motions") 
  }) 
} 

Now both user interface and server logic are ready. We can combine them together to create a shiny app and run it in the web browser.

app <- shinyApp(ui, server) 
runApp(app) 

When the parameters are modified, the plot will be refreshed automatically:

Creating a shiny app

If we set a significantly positive annualized expected return, the generated paths will tend to grow more than decline:

Creating a shiny app

Using shinydashboard

In addition to the functions shiny provides, RStudio also develops shinydashboard (http://rstudio.github.io/shinydashboard/) which is specialized in presenting data for overview or monitoring purposes.

The following example demonstrates how easy it is to create a simple dashboard to show the most popular R packages on CRAN with the most downloads in weekly and monthly time scale.

The data source is provided by cranlogs (http://cranlogs.r-pkg.org). First run the following code to install the packages we need:

install_packages(c("shinydashboard", "cranlogs")) 

Then we take a quick view of the data source of CRAN downloads:

library(cranlogs) 
cran_top_downloads() 
## No encoding supplied: defaulting to UTF-8. 
##    rank  package count       from         to 
## 1     1     Rcpp  9682 2016-08-18 2016-08-18 
## 2     2   digest  8937 2016-08-18 2016-08-18 
## 3     3  ggplot2  8269 2016-08-18 2016-08-18 
## 4     4     plyr  7816 2016-08-18 2016-08-18 
## 5     5  stringi  7471 2016-08-18 2016-08-18 
## 6     6  stringr  7242 2016-08-18 2016-08-18 
## 7     7 jsonlite  7100 2016-08-18 2016-08-18 
## 8     8 magrittr  6824 2016-08-18 2016-08-18 
## 9     9   scales  6397 2016-08-18 2016-08-18 
## 10   10     curl  6383 2016-08-18 2016-08-18 
cran_top_downloads("last-week") 
## No encoding supplied: defaulting to UTF-8. 
##    rank  package count       from         to 
## 1     1     Rcpp 50505 2016-08-12 2016-08-18 
## 2     2   digest 46086 2016-08-12 2016-08-18 
## 3     3  ggplot2 39808 2016-08-12 2016-08-18 
## 4     4     plyr 38593 2016-08-12 2016-08-18 
## 5     5 jsonlite 36984 2016-08-12 2016-08-18 
## 6     6  stringi 36271 2016-08-12 2016-08-18 
## 7     7  stringr 34800 2016-08-12 2016-08-18 
## 8     8     curl 33739 2016-08-12 2016-08-18 
## 9     9      DBI 33595 2016-08-12 2016-08-18 
## 10   10 magrittr 32880 2016-08-12 2016-08-18 

After getting familiar with the form of data we want to present in the dashboard, we can now think about constructing the dashboard in exactly the same way as constructing a typical shiny app. To make the most of shinydashboard, it is better to go through http://rstudio.github.io/shinydashboard/structure.html to get a general idea of the nice components it provides.

Similarly to shiny app, we start by creating the user interface. This time, we use dashboardPage, dashboardSidebar and dashboardBody. In the dashboard, we want to present the package download dynamics and tables of the most popular packages with top downloads in both monthly and weekly scales.

We put the menu of monthly and weekly in the side bar so users can choose which to see. In each tab page, we can put plots and tables together. In this example, we use formattable to add color bars on the download column to make the numbers more comparable and straightforward.

library(shiny) 
library(shinydashboard) 
library(formattable) 
library(cranlogs) 
 
ui <- dashboardPage( 
  dashboardHeader(title = "CRAN Downloads"), 
  dashboardSidebar(sidebarMenu( 
    menuItem("Last week",  
      tabName = "last_week", icon = icon("list")), 
    menuItem("Last month",  
      tabName = "last_month", icon = icon("list")) 
  )), 
  dashboardBody(tabItems( 
    tabItem(tabName = "last_week",  
      fluidRow(tabBox(title = "Total downloads", 
        tabPanel("Total", formattableOutput("last_week_table"))), 
        tabBox(title = "Top downloads", 
          tabPanel("Top", formattableOutput("last_week_top_table"))))), 
    tabItem(tabName = "last_month",  
      fluidRow(tabBox(title = "Total downloads", 
        tabPanel("Total", plotOutput("last_month_barplot"))), 
        tabBox(title = "Top downloads", 
          tabPanel("Top", formattableOutput("last_month_top_table"))))) 
  )) 
) 

Note that plotOutput is provided by shiny while formattableOutput is provided by formattable package. In fact, developers can create all kinds of HTML widgets that can be embedded into a shiny app as long as the package properly defines the render* function and *Output function to produce the correct HTML code.

Then we define the server logic. Since the output relies purely on the data source, we download the data before calling formattable and plot.

server <- function(input, output) { 
  output$last_week_table <- renderFormattable({ 
    data <- cran_downloads(when = "last-week") 
    formattable(data, list(count = color_bar("lightblue"))) 
  }) 
   
  output$last_week_top_table <- renderFormattable({ 
    data <- cran_top_downloads("last-week") 
    formattable(data, list(count = color_bar("lightblue"), 
      package = formatter("span",  
        style = "font-family: monospace;"))) 
  }) 
   
  output$last_month_barplot <- renderPlot({ 
    data <- subset(cran_downloads(when = "last-month"),  
      count > 0) 
    with(data, barplot(count, names.arg = date), 
      main = "Last month downloads") 
  }) 
   
  output$last_month_top_table <- renderFormattable({ 
    data <- cran_top_downloads("last-month") 
    formattable(data, list(count = color_bar("lightblue"), 
      package = formatter("span",  
        style = "font-family: monospace;"))) 
  }) 
} 

In fact, if the data is updating, we can create a dynamic dashboard where the tables and charts periodically refresh. Using ?reactiveTimer and ?reactive will be the key to achieve this. Read the documentation for more information.

Both the user interface and the server logic are ready, so we can run the app now:

runApp(shinyApp(ui, server)) 

By default, the shiny app shows the first page at the first visit. The following is a screenshot of the Last week tab page which consists of two tab panels of formattable data frames:

Using shinydashboard

The following screenshot shows the Last month tab page which consists of a histogram and a formattable data frame:

Using shinydashboard

Note

To see more examples and the code behind them, visit http://rstudio.github.io/shinydashboard/examples.html.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset