Create R Shiny Web Apps with Data Science Experience and Bluemix

Adam Massachi
Jul 13, 2017 · 8 min read

This tutorial will show you how to leverage Data Science Experience and IBM Bluemix to create and deploy R Shiny web applications. With R Shiny, you can develop clean, customized web apps with R code. Data Science Experience offers an R Studio tool; use this to develop Shiny apps in your browser. You can run your app locally within DSX to debug and experiment with your code. When you’re ready to deploy, use Bluemix capabilities to push your application to the cloud.

We’re going to develop a simple app that uses Markov models to generate sentences of arbitrary length, given a body of text. We’ll present the sentences alongside a word cloud. I’ll use two of Plato’s dialogues freely available online — Apology and Euthyphro.

Take a look at a preview of the finished app:

Working with R Studio and Shiny in DSX

R Studio is available under the Tools tab in DSX.

This will land on a new R Studio instance. Navigate to File -> New File -> Shiny Web App ...

Under Application type we'll use the common file configuration with server.R and ui.R. We'll also need to create a global.R file which contains some function and variable definitions.

When you begin a new project, the R Studio tool will create a new folder that contains all of the files relevant for your application. To help you get started, a functional example app is made available immediately. Click Run app and see for yourself.

This will launch the application in another browser tab.

We’re going to create a new Shiny app ourselves. We will develop the Shiny app within DSX, but we’ll need to move the files to our local computer later in order to push to Bluemix.

First, we’ll create ui.R, server.R, and global.R. Then, we'll add a few more:

  • manifest.yml provides important information about the app, such as name, memory, and buildpack.
  • run.R starts the application
  • Procfile indicates which commands should be run remotely
  • inti.r will install packages and dependencies (the custom buildpack expects a lower-case .r).

Note ui.R defines the user interface. server.R is an R script.
If you are unfamiliar with Shiny, I suggest that you read some of their excellent resources. Some of the code used in this article is a modification of an example simple word cloud app in the Shiny gallery. I've added code to build a Markov model which can generate sentences alongside the word cloud. I've also included the supplemental files needed for Bluemix.

For the purposes of this guide, we’ll go over each file individually, in a linear fashion. In practice, you are likely to move back and forth between files as you debug and experiment.

The Application

When you begin a new application, R Studio creates an example application for you. This is a helpful feature, but we’ll write the files ourselves of course.

  • ui.R

This is perhaps the most straightforward part of the development process. Shiny supports an intuitive, modular approach to user interface design with fluidPage. Refer back to the preview at the beginning of this article to compare the code.

library(shiny)

fluidPage(
# Application title, browser tab title
titlePanel("Plato in the Word Clouds", 'plato_clouds'),

sidebarLayout(
# Lefthand sidebar with a slider
sidebarPanel(
selectInput("selection", "Choose a dialogue:",
choices = books),
actionButton("update", "Change"),
hr(),
sliderInput("freq",
"Minimum Frequency:",
min = 1, max = 25, value = 10),
sliderInput("max",
"Maximum Number of Words:",
min = 1, max = 250, value = 100),
sliderInput("n_words",
"Words to Generate:",
min=1, max = 25, value=10),
textOutput("generated_text")


),

# Show Word Cloud
mainPanel(
plotOutput("plot")
)
)
)

We make a sidebar on the left side with a panel. Here, we put sliders which control the word cloud and the Markov sentence generator.
The first parameter in each sliderInput and textOutput is the outputId. In server.R, we can assign the output of functions to these variables so that we display text and plots which react to user behavior.

  • selectInput is a dropdown menu. We define books in the global.R file. It's an R list of dialogues and their respective file names.
  • sliderInput is a slider with integer values. We allow the users to vary some parameters of the word cloud and sentence generator.

freq controls the minimum frequency in the corpus for each word in the word cloud.

max is the maximum number of words to display on the screen

n_words is the number of words that the Markov model should generate.

  • textOutput renders the output of our function as text.
  • mainPanel renders the plot, the word cloud.

Let’s move on.

  • global.R

The objects defined in this file are visible to both ui.R and server.R -- they are in the global environment of the R session. It's not always necessary to include global.R. Properly scoping varibales is an important component of application development.

library(shiny)  
library(tm)
library(wordcloud)
library(memoise)
library(markovchain)

"Plato in the Word Clouds:
Creates word clouds for some of Plato's popular dialogues &
uses a Markov model to generate sentences of arbitrary length"


# The list of valid dialogues
books <- list("Apology" = "clean_apology",
"Euthyphro" = "clean_euthyphro")

# Use "memoise" to cache the results
getTermMatrix <- memoise(function(book) {
# stop malicious users
if (!(book %in% books))
stop("Unknown book")

# read in the text
text <- readLines(sprintf("%s.txt", book),
encoding="UTF-8")
# make a corpus for the wordcloud
# ideally your text would already be cleaned, but including this for good measure
myCorpus = Corpus(VectorSource(text))
myCorpus = tm_map(myCorpus, content_transformer(tolower))
myCorpus = tm_map(myCorpus, removePunctuation)
myCorpus = tm_map(myCorpus, removeNumbers)
myCorpus = tm_map(myCorpus, removeWords,
c(stopwords("SMART"), "and", "but"))

myDTM = TermDocumentMatrix(myCorpus,
control = list(minWordLength = 1))

m = as.matrix(myDTM)

sort(rowSums(m), decreasing = TRUE)
})
# create the markov chain
# Use "memoise" to cache the results
makeChain <- memoise(function(book){
# read in the text
text <- readLines(sprintf("%s.txt", book),
encoding="UTF-8")
terms <- unlist(strsplit(text, ' '))
fit <- markovchainFit(data = terms)
return(fit)
})

We’ve defined two functions and one other object, books.

getTermMatrix is borrowed from the Shiny word cloud resource linked above, with some minor tweaks. This function will calculate the TermDocumentMatrix after cleaning the text a bit. We'll take advantage of this for the the word cloud. A Term Document Matrix simply describes the frequency of words in a set of documents.

We use memoise to cache the results because we don't want to calculate this every time a user interacts with the app!

makeChain fits a Markov chain to the text. This captures the idea behind some text messaging applications on your phone -- suggest a new word given the previous word. To do this, we need a description of the probability that one word follows another. We memoise here again.

Continue.

  • server.R

This file provides communication between the user interface and the application. The output definitions in ui.R are sourced from here.

library(shiny)  
library(tm)
library(wordcloud)
library(memoise)
library(markovchain)

options(device='cairo')

# server
function(input, output, session) {
apol <- makeChain('clean_apology')
euthy <- makeChain('clean_euthyphro')

# generate_text
generate_text <- function(n_words){
input$update
if (input$selection=='clean_apology') {
my_fit <- apol
} else{
my_fit <- euthy
}
markovchainSequence(n=n_words, markovchain=my_fit$estimate)
}

# Define a reactive expression for the document term matrix
terms <- reactive({
# Change when the "update" button is pressed...
input$update
# ...but not for anything else
isolate({
withProgress({
setProgress(message = "Processing corpus...")
getTermMatrix(input$selection)
})
})
})

# Make the wordcloud drawing predictable during a session
wordcloud_rep <- repeatable(wordcloud)
# plot
output$plot <- renderPlot({
options(device='cairo')
v <- terms()
wordcloud_rep(names(v), v, scale=c(4,0.5),
min.freq = input$freq, max.words=input$max,
colors=brewer.pal(8, "Spectral"))})

# markov text
output$generated_text <- renderText({
generate_text(input$n_words)

})
}

First, we call makeChain on each of our texts. Note that we do this in a separate function before we generate any text.

Next, we define generate_text, which takes one parameter n_words and returns a sentence of that length from the chain with a random seed word.

Importantly, objects that we want to make available to ui.R we prepend with output${object}. Refer back the user interface file. These variable names match the outputId.

After we’ve written these files, we can run the application in DSX.

Supplemental Materials

Let’s write the remaining files.

  • manifest.yml contains important information about the application
---
applications:
- name: plato_clouds
memory: 728M
instances: 1
buildpack: git://github.com/beibeiyang/cf-buildpack-r.git
env:
CRAN_MIRROR: https://cran.rstudio.com
CF_STAGING_TIMEOUT: 9999
CF_STARTUP_TIMEOUT: 9999
  • run.R starts the application.
library(shiny)  
port <- Sys.getenv('PORT')

shiny::runApp(
appDir = getwd(),
host = '0.0.0.0',
port = as.numeric(port)
)
  • Procfile contains the code to execute run.R
web: R -f run.R --gui-none --no-save
  • init.r details the packages that we need
install.packages("shiny", dependencies=TRUE)  
install.packages("Cairo", dependencies=TRUE)
install.packages("tm", dependencies=TRUE)
install.packages("wordcloud", dependencies=TRUE)
install.packages("markovchain", dependencies=TRUE)
install.packages("memoise", dependencies=TRUE)

Each application can have its own complex set of dependencies. Further, some packages could have been built using older versions of R. You can find more information about potential issues from the R Shiny developers. Some apps are very easy to push. Others require some hacking and negotiation.

Ok, let’s deploy this app. You’ll need basic familiarity with the command line for best results.

You’ll need all of the above files in a folder, and you’ll need to change into that directory. Remember to include any other files needed for the application to run. In our case, I had previously cleaned the two text files described in the first paragraph.

  • Install Cloud Foundry command line. This open source service works with Bluemix to deploy apps.
  • Open the terminal or command prompt.

Mac — Click on the finder icon in the top right, search for terminal and open that

Linux — Depends on the distro, but you probably know where it is. In the menu look for accessories.

Windows — Click the start button, search for cmd, open.

  • Log in to Bluemix by running the following command

cf login -a https://api.ng.bluemix.net

It will prompt for your username and password.

  • Push the app!

cf push <my_app_name>

Replacing your app name with the name of app in the manifest.yml file. Make sure your app name is unique. In our case the name is plato_clouds.

Now you wait. If you’re application fails, I’ve included additional development resources on the health of the app, cf push, general considerations, and more Bluemix app failures advice.

You should run cf apps to check the status of your application.

An important note — these URLs are public-facing. The next installation of this tutorial will cover authentication and authorization.

Running cf apps provides information on all of your applications. Here, we see PRIMALITY-UNIQUE as well, a python api from another tutorial.

Finally, you can visit the page of your new app!


Originally published at datascience.ibm.com on July 13, 2017.

IBM Watson Data

Build smarter applications and quickly visualize, share, and gain insights

Adam Massachi

Written by

Watson Studio @ IBM

IBM Watson Data

Build smarter applications and quickly visualize, share, and gain insights

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade