shiny.emptystate – How To Add an Empty State Component to R Shiny
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
What happens when there’s no data to show to the user? This can occur in Shiny apps, where users must upload their own data. Well, one solution is to show an empty screen (an empty state). But this negatively impacts the user experience. A better approach would be to have a dedicated empty state screen, and today you’ll implement a Shiny empty state screen with Appsilon’s latest open-source package.
Do you love Shiny, but your apps are slow, dull, or lacking functionality? Explore the open-source Rhinoverse and take your apps to the next level.
If we’re talking about dashboards, there are numerous places where empty state screens come in handy. For example, you could use them to instruct the user to upload a dataset before proceeding with calculations. Or when there’s no data to display at all, potentially due to too many data filters being applied.
So without much ado, let’s dive straight in and leave your users in a better state!
Want to measure the performance of your new empty state changes? Try Appsilon’s shiny.benchmark package.
Table of contents:
- Building a Dashboard Logic for Shiny Empty State
- Empty State in Shiny – Dashboard in Action
- Summing up Shiny Empty State
Building a Dashboard Logic for Shiny Empty State
Before actually writing the R Shiny app, we must first discuss the logic it will implement. The app will have two screens:
- Empty state screen – Instructs the user to upload the dataset, and provides an action button to do so.
- Dashboard screen – Renders a chart (combination of a scatter and line chart) that shows the uploaded datasets and results of a fitted machine learning model.
Sounds easy enough, and in this section, we’ll deal with the dataset logic and the ways to fit a machine learning model.
To start, let’s create a dataset. It will have two columns – x
and y
, where x
is a list of numbers between 1 and 100, and y
is identical to x
, but with added variation of a number between -3 and +3:
library(comprehenr) library(ggplot2) x <- seq(1, 100) y <- to_vec(for (num in x) num + runif(n = 1, min = -3, max = 3)) df <- data.frame( x = x, y = y ) head(df)
That’s the dataset we’ll upload later to the dashboard, so it’s a good idea to save it locally. The following code snippet dumps it into a CSV file:
write.csv(df, "data.csv", row.names = FALSE)
Awesome! Now, let’s fit a simple linear regression model. The variable y
is dependent, and the variable x
is independent. R’s lm()
function allows us to easily model that relationship:
model <- lm(df$y ~ df$x) summary(model)
Here’s the summary of our model:
We’ll later extract the model coefficients, but first, let’s calculate the predictions and assign them to new dataframe columns. Put simply, we’re leveraging the patterns the model learned to make predictions on the input variable.
Wait, what is Linear Regression? Here’s our complete guide to simple and multiple linear regression in R.
In the case of linear regression, this just means solving a line equation with the coefficients from the previous image:
predictions <- predict(model, data.frame(df$x)) df$y_hat <- predictions head(df)
Here’s what the dataset looks like now:
Predictions of a simple linear regression model will always be a straight line. To demonstrate, we’ll plot the original data points and predictions.
The get_model_subtitle()
function is responsible for extracting model coefficients from the summary and formatting them as a formula string.
Below, there’s a call to ggplot()
in which original data is rendered as a scatter plot (blue), and predictions are rendered as a black line:
get_model_subtitle <- function(model) { return(paste0(round(summary(model)$coefficients[1], 3), " + ", round(summary(model)$coefficients[2], 3), "x")) } ggplot(df, aes(x = x, y = y)) + geom_point(color = "#0099F9", size = 5, alpha = 0.75) + geom_line(aes(x = x, y = y_hat), linewidth = 2) + labs( title = "X and Y Relationship", subtitle = paste("Formula:", get_model_subtitle(model)) ) + theme_minimal() + theme(plot.title = element_text(size = 20, face = "bold"))
That’s the chart we want to display in R Shiny, but only after the user uploads the dataset. In other words, only after the Shiny empty state is passed.
Empty State in Shiny – Dashboard in Action
We now have everything we need to write a dashboard in R Shiny. Well, everything except the package itself. You can install it with the following command:
remotes::install_github("Appsilon/shiny.emptystate")
The code snippet you’ll see below is simple to follow and understand, but let’s dissect it chunk by chunk:
get_model_subtitle()
– The function you saw in the previous section, it’s responsible for extracting coefficients from a linear regression model and formatting them as a string equation.empty_state_content
– HTML content that’ll be rendered while the state is empty. In our case, it includes a heading and an action button responsible for uploading a CSV file.ui
– The user interface of our app once we get passed the empty Shiny state screen. If you’re usingshiny.emptystate
package, you must include a call touse_empty_state()
.server()
– Shiny function responsible for handling application logic. It must include an instance ofEmptyStateManager
class in which you define by theid
element which element of your dashboard should be covered with the empty state content.- The
server()
function here also contains a call toshow()
andhide()
methods of the empty state manager to, well, show or hide the empty state screen depending on a condition. - Finally, the
server()
function is responsible for applying a machine learning model, calculating predictions, and displaying the chart. You might want to extract this logic when building more complex dashboards.
- The
If you prefer code over words, here’s everything you need to start using Appsilon’s shiny.emptystate
package:
library(shiny) library(shiny.emptystate) library(shinyjs) library(ggplot2) # Helper function for formatting the subtitle - model formula get_model_subtitle <- function(model) { return(paste0(round(summary(model)$coefficients[1], 3), " + ", round(summary(model)$coefficients[2], 3), "x")) } # Contents of the empty state - Just a heading and an upload button empty_state_content <- div( h3("Please upload a CSV file with columns \"x\" and \"y\""), actionButton( inputId = "upload_btn", label = "Choose CSV File", #We don't recommend this method, but for this tutorial the simple button below looks better than the default fileInput onclick = "document.querySelector('#upload').click();" ) ) ui <- fluidPage( use_empty_state(), useShinyjs(), div( id = "chart_container", plotOutput(outputId = "chart") ), shinyjs::hidden(fileInput(inputId = "upload", label = "upload")) ) server <- function(input, output) { # Initialize and show empty state empty_state_manager <- EmptyStateManager$new( id = "chart_container", html_content = empty_state_content ) empty_state_manager$show() # Handle dataset upload dataset <- reactiveVal() uploaded_dataset <- reactive({ shiny::req(input$upload) read.csv(input$upload$datapath) }) observeEvent(uploaded_dataset(), { if (nrow(uploaded_dataset()) > 0) { dataset(uploaded_dataset()) empty_state_manager$hide() } else { empty_state_manager$show() } }) # Handle chart output output$chart <- renderPlot({ shiny::req(dataset()) # ML model model <- lm(dataset()$y ~ dataset()$x) predictions <- predict(model, data.frame(dataset()$x)) ggplot(dataset(), aes(x = x, y = y)) + geom_point(color = "#0099F9", size = 5, alpha = 0.75) + geom_line(aes(x = x, y = predictions), linewidth = 2) + labs( title = "X and Y Relationship", subtitle = paste("Formula:", get_model_subtitle(model)) ) + theme_minimal() + theme(plot.title = element_text(size = 20, face = "bold")) }) } # Connect all to a Shiny app shinyApp(ui = ui, server = server)
Let’s run the app to check if everything works:
We’ve saved the dataset to a CSV file in the previous section, and used it here. As you can see, the app successfully goes from a Shiny empty state to a dashboard screen, which is just what we’ve wanted.
Let’s make a brief recap next.
Summary
R Shiny was designed to be easy for developers, but that doesn’t mean it lacks advanced functionality. This article is a perfect example of a logic you don’t want (nor need) to implement from scratch. It can be included in any app to improve the user experience and instruct the user on what to do.
Long story short, whenever you need to implement an empty state screen in your Shiny apps, look no further than shiny.emptystate. The link contains more examples that might be easier to grasp for newcomers to Shiny. If you get stuck, don’t hesitate to leave a comment below this article, or ping us via Twitter – @appsilon.
Does R Shiny sound like a promising career? Here’s how to get started.
The post appeared first on appsilon.com/blog/.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.