Introducing R Shiny web apps

Recently I’ve begun experimenting with the R Shiny package, which is a great way for interactively showcasing and sharing statistical analyses and results performed and generated in R on the web. Here I provide a simple example of how to use the shiny package to create and deploy an app to your browser using a local host. A Shiny app can also be hosted on a web server, but that is beyond the scope of this post.

Building upon a more basic app that appears in the RStudio Shiny tutorial, here we have an app that plots histograms of random samples drawn from any one of a set of common probability distributions. The user can select a distribution, a sample size, whether to overlay a smooth density curve, and only if shown, what bandwidth to use for the curve.

RshinyIntro
You can click the image to go to the app page.

This is the first of a four-part series where I add enhancements to the app in stages. Here are the other posts and corresponding versions of the app:
R sampling app version 2 [app version 2]
R sampling app version 3 [app version 3]
R sampling app version 4 [app version 4]

Apps can become much more complicated of course. The shiny package is a powerful tool for statisticians and data analysts who wish to share their work in an interactive fashion. Statistics is storytelling, and from start to finish – from the formulation of a problem or a question; through exploratory data analysis, looking at tables, charts, graphs and summary statistics; to the selection and application of appropriate advanced inferential methods; and finally the interpretation of results and dissemination of relevant information – a Shiny app can be used to tell the story interactively, in a way that is accessible to managers, clients, scientists, and anyone else who can interact with the app on a webpage but who may not be statisticians or data analysts themselves. It is an excellent web presentation tool that allows you to share your work without being present and it permits clients to focus on what aspects of a project are most interesting and important to them.

An R Shiny app consists of two scripts, ui.R and server.R, the user-interface and server-side scripts, respectively, as well as any data or other files that may be needed by the app, depending on its complexity. In this example, we need no extra files. We will generate our own data as part of the app. Here is the ui.R script:

shinyUI(pageWithSidebar(
	headerPanel("Distributions of Random Variables"),
	sidebarPanel(
		radioButtons("dist","Distribution type:",
			list("Normal"="norm","Uniform"="unif","t"="t","F"="F","Gamma"="gam","Exponential"="exp","Chi-square"="chisq","Log-normal"="lnorm","Beta"="beta")),
		sliderInput("n","Sample size:",1,1000,500),
		uiOutput("dist1"),
		uiOutput("dist2"),
		checkboxInput("density","Show density curve",FALSE),
		conditionalPanel(
			condition="input.density==true",
			numericInput("bw","bandwidth:",1)
		),
		downloadButton('dldat', 'Download Sample')
	),
	mainPanel(
		tabsetPanel(
			tabPanel("Plot",plotOutput("plot")),
			tabPanel("Summary",verbatimTextOutput("summary")),
			tabPanel("Table",tableOutput("table"))
		)
	)
))

The purpose here is not to reproduce tutorials which already exist, but rather just to show an example. If you haven’t explored Shiny before, I recommend you see the tutorial put together by the RStudio team and other online examples to get a better sense for how Shiny works, and the package documentation for a more thorough understanding of the package functions and examples of their varied uses. With at least some nominal exposure to those resources and some experience with R, it should be relatively straightforward to connect the code here to the behavior of the app and what it displays at the app image link above. The ui.R script is usually pretty simple and short even for fairly complex apps. The server.R script is the one that grows in size and complexity more linearly with your project. Here is the server.R script:

library(shiny)
rt2 <- function(n=500,dft=15){ rt(n=n,df=dft) }
formals(rgamma)[1:2] <- c(500,1)
rchisq2 <- function(n=500,dfx=1){ rchisq(n=n,df=dfx) }
formals(rf)[1:3] <- c(500,1,15)
rexp2 <- function(n=500,rate2=1){ rexp(n=n,rate=rate2) }
formals(rbeta)[1:3] <- c(500,2,2)

shinyServer(function(input,output){
	dat <- reactive({
		dist <- switch(input$dist,
			norm=rnorm,	unif=runif,	t=rt2, F=rf, gam=rgamma, exp=rexp2,	chisq=rchisq2, lnorm=rlnorm, beta=rbeta)

		def.args <- switch(input$dist,
			norm=c(input$mean,input$sd), unif=c(input$min,input$max), t=c(input$dft), F=c(input$df1,input$df2),
			gam=c(input$shape,input$rate), exp=c(input$rate2), chisq=c(input$dfx), lnorm=c(input$meanlog,input$sdlog), beta=c(input$shape1,input$shape2))
			
		f <- formals(dist);	f <- f[names(f)!="n"]; len <- min(length(f),3-1); f <- f[1:len]
		argList <- list(n=input$n)
		for(i in 1:len) argList[[names(f)[i]]] <- def.args[i]
		return(list(do.call(dist,argList),names(f)))
	})

	output$dist1 <- renderUI({
		lab <- switch(input$dist,
			norm="Mean:", unif="Minimum:", t="Degrees of freedom:", F="Numerator degrees of freedom:", gam="Shape:", exp="Rate:",
			chisq="Degrees of freedom:", lnorm="Mean(log):", beta="Alpha:")
		ini <- switch(input$dist,
			norm=0, unif=0, t=15, F=1, gam=1, exp=1, chisq=1, lnorm=0, beta=2)
		numericInput(dat()[[2]][1],lab,ini)
	})
	
	output$dist2 <- renderUI({
		lab <- switch(input$dist,
			norm="Standard deviation:", unif="Maximum:", F="Denominator degrees of freedom:", gam="Rate:", lnorm="Standard deviation(log)", beta="Beta:")
		ini <- switch(input$dist,
			norm=1, unif=1, F=15, gam=1, lnorm=1, beta=2)
		if(any(input$dist==c("norm","unif","F","gam","lnorm","beta"))) numericInput(dat()[[2]][2],lab,ini)
	})
	
	output$dldat <- downloadHandler(
		filename = function() { paste(input$dist, '.csv', sep='') },
		content = function(file) {
			write.csv(data.frame(x=dat()[[1]]), file)
		}
	)

	output$plot <- renderPlot({
		dist <- input$dist
		n <- input$n
		hist(dat()[[1]],main="",xlab="Observations",col="orange",cex.axis=1.2,cex.lab=1.2,prob=T)
		if(input$density) lines(density(dat()[[1]],adjust=input$bw),lwd=2)
	})
	
	output$summary <- renderPrint({
		summary(dat()[[1]])
	})
	
	output$table <- renderTable({
		data.frame(x=dat()[[1]])
	})
})

In the server.R script, we run any R code that we need to up front. This is the one-time stuff. You run these commands when the app launches. The code may set up certain objects in your workspace. It may load a data set or a workspace (.RData) file. Basically, this is anything that you will only need to do one time in your R session and don’t want to rerun wastefully every time a user changes some options on their screen.

After this, shinyServer is called. Within this function is where we achieve our reactivity. We use reactive expressions of various kinds to update the outputs displayed to the user based on the inputs the user selects. Reactive expressions are only evaluated when a user-initiated change to inputs is detected. These expressions are then evaluated, and immediately update the outputs sent to the browser.

Things not to get caught up on. You may notice some odd commands at the beginning of this script, specifically the use of the formals function, and you may wonder why I appear to be assigning base R functions to new objects with altered default parameters. This is not a “Shiny thing”. Ignore it. But if you must know, it’s because in putting this app together, I found that the Shiny app did not behave well the way I had originally coded it. This turned out to be due to the fact that multiple R functions I was using, e.g. rgamma and rexp, or rt and rchisq, had formal arguments with the same name. Picking parameter values in the app for one distribution, and then toggling over to another distribution in the browser which had a similarly named distribution parameter argument, would cause some annoying bugginess.

I got around this by making calls to my own wrapper functions which did not have this kind of argument name overlap. It’s possible I don’t need to do this any longer and it may have been due to the early version of Shiny, or my own lack of understanding of how Shiny worked at the time I was first experimenting with it. In any case, I got it to work. In more recent apps I have not encountered this kind of problem. Not to say that it does not remain an issue, but I have not been trying to recreate it. But if anyone can tell me what I may have been doing wrong or why this was an issue to begin with, please let me know. Moving along now…

For those of you who have dabbled in R Shiny a bit already, it is perhaps worth mentioning that in my experience I have had more success in terms of achieving complexity of reactivity and generalizability by using uiOutput in the ui.R script rather than specifying sidebar controls directly, in tandem with renderUI in the server.R script. I tend to define user controls for the sidebar in the latter script and have found so far that this style works better for my applications in general.

Now let’s discuss launching an app locally. Just place your ui.R and server.R scripts in a directory together. Launch R. Make sure the shiny package is installed. Load the package. Then all you need to do is enter runApp("C:/path/to/my/directory") for example. R will launch your app in your default web browser. Done!

In my next post, R sampling app version 2 [app], I spiff up the plots a bit by adding probability distribution function (pdf) annotations using plotmath expressions. In total there are four versions of this sampling app, one building on the next, so that you can see various elements added in stages. I didn’t have it all planned out when I began, but that’s sometimes how it goes. I’ll have more posts regarding R Shiny apps I’ve developed for various SNAP projects, to be hosted on our website like the one above. If you are interested in hosting your own apps on a web server as well, where users can interact with your apps on a webpage without having to deal with R or anything else, you’ll have to follow instructions like these.

This entry was posted by Matt Leonawicz.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: