© Matt Wiley and Joshua F. Wiley 2016

Matt Wiley and Joshua F. Wiley, Advanced R, 10.1007/978-1-4842-2077-1_13

13. Every Cloud has a Shiny Lining

Matt Wiley and Joshua F. Wiley1

(1)Elkhart Group Ltd. & Victoria College, Columbia City, Indiana, USA

When serving data to the public, easy access and interactive information make a real difference in comprehension. In this chapter, our goal is to provide access and some interesting uses for a more recent application, shiny (Chang, Cheng, Allaire, Xie, and McPherson, 2016).

Shiny is a web application framework for R. What it does is allow information consumers to interact with live data and analytics in R . This framework does not require knowledge of any web-based languages. All the same, surprisingly complex and interactive data models can be released to your clients, decision makers, or consumers.

Do you have data your customers should explore? Are there performance metrics that, transformed from static to interactive, might support your story to a board or a boss? Do your users have data they need to upload and understand better? Would you like to easily update next quarter’s data into a common dashboard? All these questions can be answered with Shiny—and since Shiny is served via a web page, if we can get Shiny onto our cloud, then anyone can benefit from the power of R.

Now, for simplicity’s sake, in this chapter we are going to live entirely on Windows inside RStudio. In the previous chapter, we got Shiny Server running on our cloud, but for now, you’ll learn on your local machine. In the next chapter, we put all the building blocks we had in this chapter into one dashboard, and we upload that to our cloud.

The Basics of Shiny

Think of Shiny as a web interface for R code. If you can do it in R, Shiny can push those results to a nice page that is viewable via Internet browsers. Even more awesomely, if a data consumer wants to interact with information, their input can be incorporated into R code.

In RStudio(RStudio Team, 2015), select File ➤ New File ➤ Shiny Web App. Name your file PieChart_Time, and under Application choose the single file option (app.R). Delete all code and type in the following code (or download from the Apress site). As you type it in, you want to notice three things. First, there is a user-interface portion of the code. Second, there is a server side to the code. Finally, the application must be run. Another aspect to notice (and this is why we recommend typing it all in manually) is where pieces of the user side and the server side mesh. In fact, in our code, there are exactly four places this happens. From the user side, variables of a number between 0 and 100, a text string, and a check box are input. The server receives those values and creates plots. It puts those plots into the output. The user side gets that output in mainPanel().

library(shiny)

# user interface drawing a pie chartShinymainPanel()
ui <- shinyUI(fluidPage(


   # Page title
   titlePanel("User Controlled Chart"),


   # Sidebar with numeric, text, and check box inputs controlled by user
   sidebarLayout(
      sidebarPanel(
         numericInput("pie",
                     "Percent of Pie Chart",
                     min = 0,
                     max = 100,
                     value = 50),


         textInput("pietext", "Text Input", value = "Default Title",
                   placeholder = "Enter Your Title Here"),


         checkboxInput("pieChoice",
                       "  I want a Pie Chart instead.", value = FALSE)
      ),


      # Show the plot(s)
      mainPanel(
         plotOutput("piePlot")
      )
   )
))


# server side R code creating Pie Chart or barplot hidden from user
server <- shinyServer(function(input, output) {


   output$piePlot <- renderPlot({
      # generate Pie chart ratios based on input$pie from user
      y <- c(input$pie, 100-input$pie)


      # draw the pie chart or barplot with the specified ratio and label

    if(input$pieChoice == FALSE){
      barplot(y, ylim = c(0,100),
              names.arg = c(input$pietext, paste0("Complement of ", input$pietext)))


    }else{
      pie(y, labels = c(input$pietext, paste0("Complement of ", input$pietext)))}


   })
})


# Run the application
shinyApp(ui = ui, server = server)

Before we delve into that code, let’s see it in action. Type and run the code that follows from the RStudio console to install Shiny on your local Windows machine:

install.packages("shiny")              

Now, go into your app.R file you carefully typed and run that code in one block. There should even be a Run App play button near the top right of the code area. Either way, something along the lines of Figure 13-1 should appear.

A393929_1_En_13_Fig1_HTML.jpg
Figure 13-1. A locally hosted Shiny App to see just what we are gaining

Go ahead and enter various numbers and text values. Play with Shiny and see what it can do. While you do so, recognize that what Shiny has done is provide an interface between you and R. Now, this particular run is hosted by RStudio on your local machine. Notice that in the console it indicates listening, and you may need to select a Stop button before you can enter any new code.

So what happened? Suppose you changed the Percent of Pie Chart number to 75. Well, that numericInput() is a reactive value. When you change it, behind the scenes, a signal is sent alerting the server that input$pie has changed. The server then notifies any reactive functions that use input$pie that something has changed. In our case, renderPlot()is a reactive function. It now knows input$pie is different, so it queries that value, gets the new value, and runs all its code again. Since that is an output value, this whole cycle repeats in reverse with output$piePlot, with the result that plotOutput()on the user side refreshes.

There are some important things to notice about all this. The process we just described runs all the code again inside renderPlot()—all of it. Now, ceteris paribus, there is no need to look up input$pietext and input$pieChoice. Our reactive function knows its values, for those are up-to-date, and while they are used again, they are not called again. Still, if we had something a little more intensive than a subtraction and some plots inside renderPlot(), we might find ourselves with a relatively steep performance cost to making any changes. While this set of code is quite compact, nevertheless you can see three places where you might put R code. One is where library(shiny) is. This is on the outside of our shinyServer(). Code that is here, such as our library call, is run once each session. A single session can have more than one user interacting with it (although that gets a bit esoteric at the moment). Still, each new user executes all the code inside the shinyServer() at least once. Finally, as we just saw, the code inside reactive functions such as renderPlot() are run once each update from any connected reactive input values. What this means is that for efficiency, consider placing as much code as possible outside the reactive functions. Moreover, if you anticipate heavy user traffic, place as much outside the server as possible too.

For now, let’s introduce some new functions. Remember, there is the user input side, there is the server side, and there is the entire Shiny app. Deep down inside, Shiny is just those three pieces. In fact, here is a rather bare-bones Shiny application :

library(shiny)

# Define user interface
ui <- shinyUI(fluidPage())


# Define server
server <- shinyServer(function(input, output) {})


# Run the application
shinyApp(ui = ui, server = server)

You can run this code and get a nice blank page. While that is perhaps less inspiring than the previous code, this helps you get coding in this interface quickly. In the user-interface section, what we see is the fluidPage() function call, inside of which we place all our user-facing text. This area of the code is the most unfamiliar part of Shiny, because this is the code that creates the HTML seen by users. In the reference section, we place links to all the Shiny UI functions. However, for this chapter, we keep to the same basic layout inside a page that is fairly self-adjusting, or fluid, depending on the browser viewing area. Notice this is an R function, and functions in R take arguments that are separated by commas. Every aspect of our growing application is contained inside this wrapper function, which takes care of all the conversion to web-ready markup. For our first app from Figure 13-1, we used a sidebar layout. This function, in turn, wants two arguments : the sidebar panel where we control our application and then the main panel where we draw it. Editing our user interface gives us the following code:

# Define user interface
ui <- shinyUI(fluidPage(
  sidebarLayout(
    sidebarPanel(),
    mainPanel()
  )
  ))

This code is a wonderful example of the power of the mathematical notion of function composition. Our goal is to eventually have something the user inputs to our R code, such as the text or check box of our example in Figure 13-1. The user inputs those values into as-yet-to-be-named functions, which give visual output, which is input into the sidebarPanel() function , which provides web output, which is input to fluidPage(), which outputs the final web user interface. We can see the results, blank though they be, in Figure 13-2.

A393929_1_En_13_Fig2_HTML.jpg
Figure 13-2. A mostly blank Shiny app, with a sidebarPanel()

From this point, we can quickly build our user’s inputs with three input functions . Each of these input functions follows a specific format, namely blargInput(input identification, text label for user readability, settings relating to the specific type of input for which blarg is a stand-in). Formally, our three input functions have these layouts, and please notice the similarities!

numericInput(inputId, label, value, min = NA, max = NA, step = NA,  width = NULL)
textInput(inputId, label, value = "", width = NULL, placeholder = NULL)
checkboxInput(inputId, label, value = FALSE, width = NULL)

Proceeding to fill in some specifics for our code, we can set the input identities for each to be useful variables for us to use later in our server code. Since we do plan to build a pie chart (and please, gentle readers, do not hate us for that choice), it makes sense to limit our numeric input to between 0 and 100. Because our server code runs once on start, it also makes sense to provide a default value for the numeric input and, indeed, the text input. Building further on our burgeoning user-interface code , we now have this:

# user interface drawing a pie chart
ui <- shinyUI(fluidPage(
  # Sidebar with numeric, text, and check box inputs controlled by user
  sidebarLayout(
    sidebarPanel(
      numericInput("pie", "Percent of Pie Chart",
                   min = 0, max = 100, value = 50),
      textInput("pietext", "Text Input", value = "Default Title",
                placeholder = "Enter Your Title Here"),


      checkboxInput("pieChoice",  "  I want a Pie Chart instead.", value = FALSE)
              ),


    mainPanel()
)
))

Again, after making those modifications, it makes sense to run the whole app again, and we see the new result in Figure 13-3. Notice that the mainPanel()is currently empty in our code, and, indeed, in Figure 13-3 we do not see anything on the right-hand side.

A393929_1_En_13_Fig3_HTML.jpg
Figure 13-3. The user interface is almost done, and the input portion in the sidebar is indeed done

Now that we have our user input captured, we are ready to use it on the server side. Remember, Shiny is an R wrapper that takes input, and when that input changes, alerts a reactive function on the server side that something is different. Behind the scenes, Shiny is taking care of knowing when the input changes, and alerting the correct reactive functions on the server; we need not worry about such things. Our server defines a new function that takes both input and output as formals, and uses those in its body. If needed, take a quick look back at Chapter 4 to refresh your memory on just what a function is. Since all we want to do is draw either a bar plot or a pie chart, the majority of our code does precisely that in very common R code. In fact, the only part that is not fairly normal R code is our reactive function renderPlot(). Since we wish to give graphical outputs, we use that function.

Reactive functions are always waiting for Shiny to alert them to any changes in the inputs from the user-interface side. When an input changes, the reactive functions are notified if they have at least one of the changed values in their body. If they do, then the reactive functions run their code again, this time with the new value(s). We see this in the following code. The only other part of this is that the output of renderPlot()is stored in an output variable, and we choose that name to be piePlot (even though it might not output a pie chart, depending on the check box choice—which is wholly our fault for selecting a poor name).

# server side R code creating Pie Chart or Bar plot
server <- shinyServer(function(input, output) {


   output$piePlot <- renderPlot({
      # generate Pie chart ratios based on input$pie from user
      y <- c(input$pie, 100-input$pie)


      # draw the pie chart or barplot with the specified ratio and label

    if(input$pieChoice == FALSE){
      barplot(y, ylim = c(0,100),
              names.arg = c(input$pietext, paste0("Complement of ", input$pietext)))


    }else{
      pie(y, labels = c(input$pietext, paste0("Complement of ", input$pietext)))}


   })
})

That is essentially it. When our server runs, it alerts the user-interface function plotOutput("piePlot")to the new image. This function lives in the mainPanel() region. Under this simple model of Shiny, the sidebar is where user input is received, the server side is where it is processed behind the scenes by R, and the main panel is where the processed output is viewed by the user. Just this once, we show the full HTML code that Shiny creates for us to serve in Figure 13-1:

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>  <script type="application/shiny-singletons"></script>  <script type="application/html-dependencies">json2[2014.02.04];jquery[1.11.3];shiny[0.13.1];bootstrap[3.3.5]</script><script src="shared/json2-min.js"></script>
<script src="shared/jquery.min.js"></script>
<link href="shared/shiny.css" rel="stylesheet" />
<script src="shared/shiny.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="shared/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<script src="shared/bootstrap/js/bootstrap.min.js"></script>
<script src="shared/bootstrap/shim/html5shiv.min.js"></script>
<script src="shared/bootstrap/shim/respond.min.js"></script>  <title>User Controlled Chart</title>
</head>
<body>
  <div class="container-fluid">
    <h2>User Controlled Chart</h2>ShinyHTML code, creation
    <div class="row">
      <div class="col-sm-4">
        <form class="well">
          <div class="form-group shiny-input-container">
            <label for="pie">Percent of Pie Chart</label>
            <input id="pie" type="number" class="form-control" value="50" min="0" max="100"/>
          </div>
          <div class="form-group shiny-input-container">
            <label for="pietext">Text Input</label>
            <input id="pietext" type="text" class="form-control" value="Default Title" placeholder="Enter Your Title Here"/>
          </div>
          <div class="form-group shiny-input-container">
            <div class="checkbox">
              <label>
                <input id="pieChoice" type="checkbox"/>
                <span>  I want a Pie Chart instead.</span>
              </label>
            </div>
          </div>
        </form>
      </div>
      <div class="col-sm-8">
        <div id="piePlot" class="shiny-plot-output" style="width: 100% ; height: 400px"></div>
      </div>
    </div>
  </div>
</body>
</html>

So now, go back and carefully look through the very first block of code we showed in this section that was the entire Shiny app and realize that code creates all of the preceding HTML and JavaScript. That is all there is to Shiny. Now, in this example, our R code was not particularly impressive. All the same, it is the principle that counts. Users can now interact with data, and it is not impossible to imagine more-interesting applications.

In the next few sections, we introduce a handful of other interesting and useful Shiny input functions along with some essential reactive functions. Finally, in the end, we upload all of them to our cloud so that you can see them live on the Web.

Shiny in Motion

Now that we have a better appreciation for what Shiny does, it is important to appreciate the dynamic potential of a web page over print reports. One of the authors is a mathematics professor, and one of the challenges in teaching mathematics is that once data goes beyond three dimensions, it becomes difficult for students to conceptualize that information. Naturally, students, being people, are not unique in their high-dimensional blindness. Time can provide a fourth dimension, and Shiny can provide us that time.

Another feature of Shiny we mentioned earlier, is that R code makes sense to place in three distinct locations. The first is at the beginning of all the code, before user-interface or server-side code is written. Code placed in this area runs once per instance. While the value of this may not be as clear on a local machine, when your code is hosted on a cloud, a particular R session may have more than one user interacting with it. That is the way the Shiny server we installed in the preceding chapter works. This is a great place to read in any static data or perform any once-only calculations. It is the most computationally efficient. While we do not do so in this example, the code may also be placed inside the server code but outside any reactive functions that would be run once per user connection to a session. Finally, the code inside reactive functions, inside the server function, is executed once every time any input data that the function references is changed.

Let’s take a look at how to change data with respect to time. From the user-interface side, there is an input function that is a number-line slider bar. As with all input functions, we need to give it a name so that our reactive functions can know where to look for input updates. As with our numericInput()from the prior section, we give this function some minimum and maximum values in years. Finally, we are going to animate our sliderInput() function . Take a look at the code we use inside our user interface. Our code uses an animate option that includes an interval in milliseconds and also has the animation keep on looping.

sliderInput("ayear", "Academic Year:",
                     min = 2014, max = 2017,
                     value = 2014, step = 1,
                     animate = animationOptions(interval=600, loop=T) )

While this application also shows how to perform R calculations on both static data and user inputs, it really is quite simple again from the R code perspective. In fact, other than the titlePanel() command and of course our slider code, this is quite a bit like our first Shiny application . We show both the code and the end result in Figure 13-4.

A393929_1_En_13_Fig4_HTML.jpg
Figure 13-4. A slider bar input that has a Play button feature showing data in motion
library(shiny)
slider_data<-read.csv("slider_data.csv", header = TRUE, sep = ",")
Phase1    <- slider_data[,2]
Phase2    <- slider_data[,3]
Phase3    <- slider_data[,4]
Phase4    <- slider_data[,5]


# Define UI for application that draws Bar Plot
ui <- shinyUI(fluidPage(


   # Application title
   titlePanel("Training Programme Results"),


   # Sidebar with a slider input for number of bins
   sidebarLayout(
      sidebarPanel(
sliderInput("ayear", "Academic Year:",
                     min = 2014, max = 2017,
                     value = 2014, step = 1,
                     animate = animationOptions(interval=600, loop=T) )  ),


      # Show the bar plot
      mainPanel(plotOutput("barPlot") )               )               ))


# Define server logic required to draw a barplot
server <- shinyServer(function(input, output) {


   output$barPlot <- renderPlot({
      # Count values in each phase that match the correct date.
     cap<-input$ayear*100
     x <- c(sum(Phase1<cap), sum(Phase2<cap), sum(Phase3<cap), sum(Phase4<cap))


      # draw the barplot for the correct year.
      barplot(x, names.arg = c("Phase I", "Phase II", "Phase III", "Fellows"),
              col = c("deeppink1","deeppink2","deeppink3","deeppink4"),  ylim=c(0,50))
   })           })


# Run the application
shinyApp(ui = ui, server = server)

Uploading a User File into Shiny

Our last example for this chapter indicates that users can upload files via Shiny that can then be processed via R. Of course, unlike our first two examples, this is a shade riskier because such uploaded files are not under our control. The data might be any file and not truly work with our results. It is possible to build various error-checking features into code; in fact, we mentioned such techniques in part while discussing functions. However, to focus our code on strictly new features, we pretend for a moment that we live in a perfect world. We have a sample file on the Apress website for this book that is just a comma-separated values file with the numbers 1 through 10.

Perhaps unsurprisingly, to receive a file input, the command in Shiny is fileInput().Other than inputId, maybe the most interesting feature is that this could even accept multiple uploads, although in our example we leave that to its default value of FALSE. Uploading more than one file might not be supported in all browsers. Also in the user-interface area, we use a new output function, tableOutput(), which along with plotOutput() allows us to have both chart and graphical outputs for our users. Before we talk about the server-side code that enables us to interact with our user’s file, we take a look at what our CSV file upload gives us. Figure 13-5 is the view after we uploaded our CSV file, and we show the code for just the user interface:

A393929_1_En_13_Fig5_HTML.jpg
Figure 13-5. File upload results
ui <- shinyUI(fluidPage(

      fileInput("file", label = h3("Histogram Data File input")),
      tableOutput("tabledf"),
      tableOutput("tabledf2"),
      plotOutput("histPlot1")  ))

First of all, notice the first table. It contains four variables that are the results of directly accessing the input$file variable. This might be counterintuitive based on our other examples, where the user inputs could be directly accessed from their input$inputId. However, it does show that our user input is, in fact, a data frame, and the temporary data path is available to access just as any file location might be. Fair warning: If a user uploads another file, since we are in a temp directory, our old file might well be overwritten. Next, notice that our file’s actual data is in the second table, and as shown in the histogram, that information is available for any R computations we might choose to code.

Let’s next focus on the server side, because this is not as simple as our prior examples. Sometimes, you have input that takes a little more managing than a single numeric or text value. Because that data is input data, it needs a reactive function to be alerted if it has changed. However, you might prefer to use fairly clean code. Fortunately, Shiny offers a basic reactive function named, well, reactive(). In the following code, we create our own reactive function to cope with a new scenario, namely, the one we wanted. When a file is uploaded, Shiny alerts our reactive function reactive() that input$file has changed. The function naturally queries the input to see what it is now, and stores that data frame into the variable file1. We would like access to the data inside our CSV file, so we need to call read.csv() on that file, which naturally needs to know the file’s location. That is stored in the last column, named datapath. What we get is an object that we can treat like a data frame later in our code.

histData<-reactive({
  file1<-input$file
  read.csv(file1$datapath,header=TRUE ,sep = ",")      })

Other than this, we can use our data now inside other reactive functions, familiar ones even. We show one more new reactive function, renderTable(), as well as a familiar one, renderPlot(). You should also draw your attention to histData()$X1. Notice that this has function parentheses, yet elements are still accessed via the familiar $. This is built with a reactive function, and as such, we must acknowledge that it is both a function that takes input from the user as well as a data storage object with data to access.

output$tabledf2<-renderTable({
  histData()            })


output$histPlot1<-renderPlot({
  hist(as.numeric(histData()$X1))              })

That wraps up our analysis of the code. For completeness, we end this section with the entire code, but now you have the techniques to be up and running in Shiny. Before this section ends, we point out that the references section has as its first link a reference site for most or many Shiny functions. If there is something you want to do, check there first. It is quite intuitive. Also of note is that the next section of this chapter shows how to upload an application file to the cloud and get it working live. Finally, notice the h3() function inside the fileInput() function in the user interface. Shiny has a whole collection of functions that duplicate various common HTML features such as headers. For this chapter, we consider those adiaphora. In Chapter 14, we focus more on making our user interface, for lack of a better word, pretty.

library(shiny)

ui <- shinyUI(fluidPage(

      # Copy the line below to make a file upload manager
      fileInput("file", label = h3("Histogram Data File input"), multiple = FALSE),
      tableOutput("tabledf"),
      tableOutput("tabledf2"),
      plotOutput("histPlot1")


))

# Define server logic required to draw a
server <- shinyServer(function(input, output) {


  output$tabledf<-renderTable({
    input$file
  })


histData<-reactive({
  file1<-input$file
  read.csv(file1$datapath, header=TRUE ,sep = ",")     })


output$tabledf2<-renderTable({
  histData()           })


output$histPlot1<-renderPlot({
  hist(as.numeric(histData()$X1))               })


})

# Run the application
shinyApp(ui = ui, server = server)

Notice that in this code, we did not focus on distinctions such as side panels or main panels; instead, we had our user-interface side (both the requested input and the generated output) all in one hodgepodge. It is worth noting that if you have a side panel, a main panel is needed to hold any output that is not hosted by the side panel.

Hosting Shiny in the Cloud

Our last task is to get our files from a local machine to the cloud instance. Remember, in previous chapters, we have used not only PuTTY (Tatham, 2016) to install Shiny Server on our cloud, but also WinSCP (Prikryl, 2007) to move files up to our cloud. We start WinSCP, reconnect, and ensure that on the left, local side we are in our Shiny application folder, while on our right, cloud server side, we are in /home/ubuntu. We show the result of that process in Figure 13-6.

A393929_1_En_13_Fig6_HTML.jpg
Figure 13-6. WinSCP with PieChart_Time, slider_time, and Upload_hist Shiny applications all uploaded

From here, we access PuTTY and run the following code from the command line, one at a time:

sudo cp -R ∼/PieChart_Time /srv/shiny-server/              
sudo cp -R ∼/slider_time /srv/shiny-server/
sudo cp -R ∼/Upload_hist /srv/shiny-server/

Which leaves us with one final step. Recall, back when we first set up our cloud instance, we carefully allowed just a few ports to work for just a few IP addresses. You need to log back into AWS and get into the EC2 Dashboard again. From there, on the left navigation menu, go back to Security Groups and select your AdvancedR group. Once that group is selected, click the Inbound tab and then click Edit ä Add Rule. You want to use a Custom TCP Rule, and add port 3838, and finally lock it to your IP address. Then click Save. The result looks like Figure 13-7.

A393929_1_En_13_Fig7_HTML.jpg
Figure 13-7. The AWS Inbound table with Shiny Server port 3838 open to one single local IP address

You may now verify that your apps work by typing the following URLs into any browser:

http://Your.Instance.IP.Address:3838/PieChart_Time/

http://Your.Instance.IP.Address:3838/slider_time/

http://Your.Instance.IP.Address:3838/Upload_hist/

Should you wish to allow others or the world to access your applications, change the Inbound source IP address to 0.0.0.0/0 only for port 3838. It is a security measure not to allow the world access to your SSH port. Of course, if you are now hosting an open-to-the-world port 3838, your server is visible and known. It can be beneficial to have different cloud instances for individual activities. We recommend against having confidential data on public-accessible servers. Again, cloud security is beyond the scope of this book.

Final Thoughts

We seem to have spent some time discussing cloud security. There are two reasons behind this. The first is simply our paranoid natures. However, a fair bit of data can contain confidential information. The ethical use of data, including reasonable safeguards, is important. In recent months, AWS has taken great strides to create safer environments for EC2. As always, security is a matter of reasonableness. The more vital your data, the better steps may be warranted to protect that information.

Those concerns aside, this is truly an unprecedented capability, to allow users to interact with data analytics in such an open fashion. Yes, in our experience, this can lead some data consumers to make uneducated decisions. Still, the benefit of the transparency possible through techniques such as Shiny far outweigh the disadvantages. It is an exciting world, one we are eager to explore.

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

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