The world’s leading publication for data science, AI, and ML professionals.

Building your First Shiny app in R

Learn how to build a shiny app using R, and showcase your code and work interactively

[Disclaimer: This post contains some affiliate links to my Udemy Course]

So, you’ve developed your Data Science model or analysis using R, and now you are, maybe, thinking that you would like to showcase the results in a visual and intuitive way.

You’ve tweaked a bit of your storytelling, added some plots but you feel that your visuals are a bit static, making you repeat code or plots throughout the storytelling process. Also, you are having a hard time hiding your code from the output, something critical, that you know will cause some confusion with business or non-technical users.

Luckily, you have [shiny](https://shiny.rstudio.com/)! shiny is a great library to build R apps where you can embed R code and results, without having to build your own front end and back end. shiny features are amazing, enabling you to serve apps instantly, while letting your users interact with results from your models or analysis.

Another cool thing about shinyis that it can be used directly from R, just like any other library – using R code, we can set up a basic HTML interactive page, that can be used to show plots, DataFrames or other elements.

In this post, we’ll explore a bit of the Shinylibrary, with the goal of helping you build your first app. We’ll use several examples, like:

  • Building a simple ‘hello world app’, to understand back end and front end flow.
  • Building a simple scatter plot app.
  • Building an app with three different views, including some results from a decision tree.

At the end of the post, you should be ready to work with this library using R. Although shiny will take some time to master, what we’ll learn here will, hopefully, be a good kick-off to get you started on the intricacies of using shiny apps, as well as connecting your dataframes and plots with it.

Let’s get started!


Our first shiny Vanilla App

Let’s start by installing shinyin our environment:

install.packages('shiny')

After installing the shinypackage, we can load it in our environment using the usuallibrary function:

library(shiny)

For starters, we’ll declare afluidPage. This function will be our main way to dictate and interact with the front end of the web app. In its core, fluidPageis a pretty standard row-column format user interface (it even includes some bootstrap features), and that should be enough for most basic data products. It’s also generally tweakable, being able to incorporate a lot of sections inside the app, as we will see in a couple of minutes.

In a nutshell,fluidPage can be considered the front-end layer of our shiny app. This is where we will build everything that the user will see, and how it will interact with the back end.

Our first page is a pretty basic one that just prints "My first shiny page!" :

sp <- fluidPage(
  "My first shiny page!"
)

To run our app, we also need to setup our back end – this can be done by defining a custom function that will be passed to our shinyApp . For now, our server function will be completely empty (we are not using anything on the back end, at the moment):

server <- function(input, output) {
}

Using shinyApp , we can launch our first app by passing the front end (sp) back end layers (server):

shinyApp(sp, server)

After running the code above, a new window should pop up! This window is where our shiny app is being served:

Our app is being served locally in our computer on the local host http://127.0.0.1:7388/. You should see your local host address right below theshinyApp command on the R console:

While you have the app running, you can also type the address on your browser, and that will also take you to your app.

Of course, our app is useless, at the moment – we don’t have any input or output, and it would be useful to be able to interact with our app! Let’s start by building a simple application with a 2D scatter plot, using the mtcars DataFrame, next!


Building an Interactive Plot App

Of course, we are not only tied with writing a basic text app. For instance, imagine we would like to show, on our app, a plot similar to this one:

I’m using the toy dataset mtcars to plot a scatter plot of the weight of the car vs. gas consumption (miles-per-gallon):

library(ggplot2)

ggplot(
  data = mtcars,
  aes(x=mpg, y=wt)
) + geom_point(color='darkgreen')

A cool thing we could do is serve a scatter plot inshiny app. Additionally, and to make our app a bit more interesting, we would like to have dynamic x and y columns, using inputs from the user.

Although it seems pretty hard to do this, we can do it simply by changing some portions of our back and front end layers. Let’s start by making the user select two columns from the list of columns available in mtcars :

sp <- fluidPage(

  selectInput(
    inputId = 'var1',
    label = 'Column X Axis',
    choices = colnames(mtcars)
  ),

  selectInput(
    inputId = 'var2',
    label = 'Column Y Axis',
    choices = colnames(mtcars)
  )

)

The selectInput function creates a dropdown box with all the columns available in mtcars . selectInput is one of the many available input methods in shinyand we can pass colnames(mtcars) to the choices argument, that will take care of filling our dropdown with all the elements.

The inputId is extremely important in most shiny apps – it’s the way we have to send information between the front end and back end. In this case, everytime we point to a var1 or var2 in the back end, R will pick up the value that we have stated in the dropdown box. In certain scenarions, our variables coming from the front end must be included in a reactive context, something we will see on the third app we will build.

If we just give this new sp to our app, we have something new on our front end:

shinyApp(sp, server)

Interesting! Our front end now has two dropdown boxes where we can choose the columns for our X and Y axis. But.. how can we give these elements to our plot? That’s where the back end layer comes to the rescue:

server <- function(input, output) {
  output$scatter <- renderPlot({
      ggplot(
      data = mtcars,
      aes_string(x=input$var1, y=input$var2)
    ) + geom_point(color='darkgreen')
  })
}

Notice that we can use the standard input and output arguments. When we use the server function in the context of a shinyApp , input and output will be resolved inside the app. For instance, input$var1 will pick data from the first selectInput in our page – because this first selectInput has id var1 .

When we now run our shinyApp, our app will look a bit different:

shinyApp(sp, server)

We have our plot available in our shiny app! In the current view, the scatter plot does not say much as we are plotting mpgagainst mgp , the same variable. The neat thing is that if we change our columns in the dropdown, our plot will update, automatically:

Cool! So, basically, everytime we switch our selectInput with id var1, the stored column is stored on the variable and fed to the input$var1 on our server. The same reasoning goes for input$var2and var2. Logically, the ids are the reference we use to connect the our back end and front end layers.

We can also tweak our app a bit further, in terms of front end. For instance, we can split our layout into a side by side app, using sidebarLayout and wrapping each section in a sidebarPanel and mainPanel :

sp <- fluidPage(

  sidebarLayout(

    sidebarPanel(
      selectInput(
        inputId = 'var1',
        label = 'Column X Axis',
        choices = colnames(mtcars)
      ),

      selectInput(
        inputId= 'var2',
        label = 'Column Y Axis',
        choices = colnames(mtcars)
      )
    ),

    mainPanel(
      plotOutput("scatter")
    )
  )
)

Our app will now have the following look:

Our new layout is a two column format plot, that we can manipulate. We’ve chosen a sideBar layout, but, there are more layouts available at our disposal, available here.


Building an App based on a Model

We’ve built our first usefulshiny app using a plotting application! The main layers we’ve looked into were:

  • A back end we could create using our customserver function.
  • A front end we could design using fluidPage

This mechanic is repeated for most single page applications we can develop inside shiny. If you are curious about multiple page applications, you can also check shiny.router.

For our last example, let’s serve a basic decision tree model using shiny with a three column layout.

First, we’ll train a decision tree on top of our mtcarsdata, trying to predict the mpg based on the wt , cyl and hp :

library(rpart)

dtree <- rpart(data=mtcars, mpg ~ wt + cyl + hp,
               control = list(minsplit=1))

This is, of course, a laughable decision tree and model training process, that doesn’t even contain a proper train-test split. The goal here is just to use a basic model in the context of shinyso let’s ignore those details, for now.

Let’s save this dtree object into a rds file:

saveRDS(object=dtree, file='decisiontree.rds')

Imagine we would like to build the following app:

Here, we would need three columns, an excellent use case for a mix of fluidRow and column . Let’s start by building a three column layout, starting with our front end:


sp <- fluidPage(

  fluidRow(

    column(4, 'Column 1!'),

    column(4, 'Column 2!'),

    column(4, 'Column 3!')       
  )
)

server <- function(input, output) {
}
shinyApp(sp, server)

Our shinyApp will have the following look:

Notice that our Column 1! , Column 2! and Column 3! are spaced equally on the app’s front end. The cool thing about fluidRow and column is that they behave in a "boostrapy" way, meaning that, if we don’t use full screen, our columns will be stacked, instead of crunching data into something that is not readable.

Starting with the first column, we’ll want to have a list of all the cars available, so that we can select which car we will highlight on the center column – doing this is easy as we’ve already learned selectInput :


sp <- fluidPage(

  fluidRow(

    column(4, 
        selectInput(
          inputId = 'selectedcar',
          label = 'Car List',
          choices = rownames(mtcars)
        )
    ),

    column(4, 'Column 2!'),

    column(4, 'Column 3!')       
  )
)

server <- function(input, output) {
}
shinyApp(sp, server)

On the left column, we’ll now be able to select specific cars:

On the second column, we would like to set a plot of our predictions with the selected car highlighted. We need several things to do this, namely interacting with the back end:

  • Load our model and predict all cars weight.
  • Build a two Dimensional plot of the MPG vs. HP (if you want, you can also add another dropdown that chooses the second variable)
  • Highlight the scatter with the selected car.

Here’s an example of how we can build a back end that will match those requirements:

server <- function(input, output) {
  model <- readRDS('decisiontree.rds')
  predictions <- data.frame(cbind(
    mtcars$hp,
    predict(object = model, mtcars)
  ))

  colnames(predictions) <- c('hp','pred_mpg')

  sc <- reactive({
    selected_car <- input$selectedcar
  })

  output$scatter <- renderPlot({

    select_car <- sc()

    (
      plot_all_cars <- ggplot(
        data = predictions,
        aes(x=hp, y=pred_mpg)
      ) 
      + geom_point(colour="darkgreen") 
      + geom_point(data=predictions[select_car, ], aes(x=hp, y=pred_mpg), colour="red", size=5)
    )
  }) 

Let’s detail our back end code step-by-step:

  • First, we load our model using model<-readRDS('decisiontree.rds') and use the predict function to get the predictions from our model.

  • Then, we define the colnames of our predictions object.
  • After that, we set a reactive object. A reactive object is an object that will affect any other object (requiring calculations). For instance, in this case, the select_car will affect the filter applied to the dataframe in one of the geom_point plots. As this will require some calculation to happen in the background, we need to set a reactive context on this object, defining it to be called with the function sc() .
  • Finally, we are defining plot_all_cars with two geom_point: one for all cars and one (that will serve as the highlight) for the car we will select. Notice that select_car will contain the returning reactive object from sc , the object we will load using the dropdown on the left.

Let’s check the look of our app now:

If we highlight another car, our plot’s highlight changes:

On the front end, we just had to change our second column, feeding it a plotOutput module:

sp &lt;- fluidPage(

  fluidRow(

    column(4, 
        selectInput(
          inputId = 'selectedcar',
          label = 'Car List',
          choices = rownames(mtcars)
        )
    ),

    column(4, 
           plotOutput('scatter')
           ),

    column(4, 'Column 3!')       
  )
)

Only one thing left! Plotting our decision tree on the right column – let’s do that on our backend, as well but, first, we need to load rattle library:

library(rattle)

And then, applying some changes to our back end and front end layers:

server &lt;- function(input, output) {
  model &lt;- readRDS('decisiontree.rds')
  predictions &lt;- data.frame(cbind(
    mtcars$hp,
    predict(object = model, mtcars)
  ))

  colnames(predictions) &lt;- c('hp','pred_mpg')

  sc &lt;- reactive({
    selected_car &lt;- input$selectedcar
  })

  output$scatter &lt;- renderPlot({

    select_car &lt;- sc()

    (
      plot_all_cars &lt;- ggplot(
        data = predictions,
        aes(x=hp, y=pred_mpg)
      ) 
      + geom_point(colour="darkgreen") 
      + geom_point(data=predictions[select_car, ], aes(x=hp, y=pred_mpg), colour="red", size=5)
    )
  })

  output$dtree_plot &lt;- renderPlot({
    fancyRpartPlot(model, sub='')
  })

}

sp &lt;- fluidPage(

  fluidRow(

    column(4, 
        selectInput(
          inputId = 'selectedcar',
          label = 'Car List',
          choices = rownames(mtcars)
        )
    ),

    column(4, 
           plotOutput('scatter')
           ),

    column(4, 
           plotOutput('dtree_plot')
          )       
  )
)

Super simple! To the back end code above, we added:

output$dtree_plot &lt;- renderPlot({
    fancyRpartPlot(model, sub='')
  })

After referencing it in our front end, our final app looks like the following:


As you can see, building shiny apps in R is pretty straightforward. In this post, we’ve checked some important components, such as:

  • Building the front end of our application, using fluidPage .
  • Building the back end of our application, using a custom function server .
  • Connecting our front end and back using ids and reactive elements.
  • Using different UI elements such as fluidRow or sidebarPanel .

I hope this post gave you some ideas on cool shiny apps you can develop for your projects. As a conclusion:shiny contains a bunch of tweakable elements, and it’s very difficult to know all of them by heart. One of the more important pages you can save is shiny articles, the official documentation of the library that will guide you on other stuff we didn’t cover throughout this post.

Thank you for taking the time to read this post!

Here’s the full code for the apps we’ve built:

library(shiny)

# First App

sp &lt;- fluidPage(
  "My first shiny page!"
)
server &lt;- function(input, output) {
}
shinyApp(sp, server)

# Second App
sp &lt;- fluidPage(

  sidebarLayout(

    sidebarPanel(
      selectInput(
        inputId = 'var1',
        label = 'Column X Axis',
        choices = colnames(mtcars)
      ),

      selectInput(
        inputId= 'var2',
        label = 'Column Y Axis',
        choices = colnames(mtcars)
      )
    ),

    mainPanel(
      plotOutput("scatter")
    )
  )
)

server &lt;- function(input, output) {
  output$scatter &lt;- renderPlot({
      ggplot(
      data = mtcars,
      aes_string(x=input$var1, y=input$var2)
    ) + geom_point(color='darkgreen')
  })
}
shinyApp(sp, server)

# Training model
library(rpart)

dtree &lt;- rpart(data=mtcars, mpg ~ wt + cyl + hp,
               control = list(minsplit=1))

# Save dtree file
saveRDS(object=dtree, file='decisiontree.rds')

# Third App
sp &lt;- fluidPage(

  fluidRow(

    column(4, 
        selectInput(
          inputId = 'selectedcar',
          label = 'Car List',
          choices = rownames(mtcars)
        )
    ),

    column(4, 
           plotOutput('scatter')
           ),

    column(4, 
           plotOutput('dtree_plot')
          )       
  )
)

server &lt;- function(input, output) {
  model &lt;- readRDS('decisiontree.rds')
  predictions &lt;- data.frame(cbind(
    mtcars$hp,
    predict(object = model, mtcars)
  ))

  colnames(predictions) &lt;- c('hp','pred_mpg')

  sc &lt;- reactive({
    selected_car &lt;- input$selectedcar
  })

  output$scatter &lt;- renderPlot({

    select_car &lt;- sc()

    (
      plot_all_cars &lt;- ggplot(
        data = predictions,
        aes(x=hp, y=pred_mpg)
      ) 
      + geom_point(colour="darkgreen") 
      + geom_point(data=predictions[select_car, ], aes(x=hp, y=pred_mpg), colour="red", size=5)
    )
  })

  output$dtree_plot &lt;- renderPlot({
    fancyRpartPlot(model, sub='')
  })

}

shinyApp(sp, server)

If you would like to drop by my R courses, feel free to join here (R Programming for Absolute Beginners) or here (Data Science Bootcamp). My R courses are suitable for beginners/mid-level developers and I would love to have you around!


Related Articles