Shiny is a very convenient tool that helps us create an app using R. It provides a wide range of layouts and widgets that you can add to an app. Common features in shiny may have been explained in many tutorials and courses, but other features that are more advanced require more exploration from the users with only brief documentation. In this article, let’s discuss some advanced features in shiny that commonly asked by users that may help you build better app.
What We Will Learn
From the previous article, we have explored various ways in improving our User Interface. In this second article, we will be focusing on Reactive Elements in Shiny! Below are the topics that we will explore:
- Using
reactive()
- Action button
eventReactive()
vsobserveEvent()
- Reactive UI
Using reactive()
A reactive expression is an R expression that uses widget input and returns a value. The reactive expression will update this value whenever the original widget changes. - Shiny Get Started
Simply said, a reactive expression help us to create a value–or in most cases, a data–based on the input given. This allow us to effectively use codes, so that the data that needs to be used in multiple render function can be made only with a single run. Below is example.
When we have retail
data, that records online purchases of a store and plan to visualize its trend and also displays the total unique purchases and total sales obtained throughout a period. We can prepare one data that will be used for all of our outputs.
In UI, we can prepare inputs and outputs as usual:
# UI
sidebarLayout(
sidebarPanel(
# select period
dateRangeInput("date", label = "Sales Period:",
min = min(retail_clean$invoice_date),
max = max(retail_clean$invoice_date),
start = min(retail_clean$invoice_date),
end = max(retail_clean$invoice_date)),
# select purchase status
selectInput("status", label = "Status of Purchase:",
choices = c("Purchased", "Cancelled"),
selected = "Purchased", multiple = T),
# output 1
h2(strong(textOutput("unique_purchases"))),
h5("Total Unique Purchases"),
# output 2
h2(strong(textOutput("total_sales"))),
h5("Total Sales")
),
mainPanel(
# output 3
plotlyOutput("trend_line", height = "450px")
)
)
In the server, we will first use reactive expression reactive()
to create data. In this example we store it in trend_data
. After that, the reactive data can be called just like calling a function trend_data()
in each of our render function.
# SERVER
## prepare reactive data
trend_data <- reactive({
# `validate()` is additional; to prepare friendly message for error
validate(
need(input$status != "", "Please fill all inputs provided.")
)
retail_clean %>%
filter(status %in% input$status,
invoice_date >= input$date[1],
invoice_date <= input$date[2]) %>%
mutate(invoice_dt = floor_date(invoice_date, unit = "week"))
})
## output 1
output$unique_purchases <- renderText({
overview <- trend_data()
scales::comma(length(unique(overview$invoice)))
})
## output 2
output$total_sales <- renderText({
overview <- trend_data()
scales::comma(sum(overview$sales))
})
# output 3
output$trend_line <- renderPlotly({
plot_line <- trend_data() %>%
group_by(invoice_dt) %>%
summarise(n_purchase = n()) %>%
mutate(text = glue("Date: {invoice_dt}
Number of Purchases: {n_purchase}")
) %>%
ggplot(aes(x = invoice_dt, y = n_purchase)) +
geom_line(lwd = 0.5) +
geom_point(aes(text = text), color = "salmon", size = 3) +
scale_y_continuous(labels = scales::comma) +
labs(x = NULL, y = NULL,
title = "Trend of Weekly Purchases") +
theme_minimal()
ggplotly(plot_line, tooltip = "text") %>%
layout(title = list(x = 0.5)) %>% # adjust title to the center
config(displayModeBar = F) # removing menu bar
})
Notice that we do not have to repeat the same code again (filter data based on inputs) in each of our outputs because we already use the reactive expression above. This will ease our work in Shiny especially when creating many outputs.
Additionally, there is also validate()
that help us prepare friendly error message in case of error caused by users do not provide all the inputs needed. In this case it says, If the input$status
is blank, give the message “Please fill all inputs provided.”.
Below is the result:
Reactive UI
selectInput is already a common widget in Shiny but selectInput that can provide reactive options based on other inputs is quite interesting. To do that, we need to get familiar with uiOutput()
and renderUI()
. Both function works relatively similar to any outputs function in Shiny, only this time we will create UI as an output.
In our data, we have column named “country” that records the country where the sales come from and “category_id” that records the category id of each product purchased. We will try to obtain the top purchased product per country and category id. This time, the options for category id will be based on the country input. Let’s create the code below:
# UI
# continuing from last code, still in TAB 2
fluidPage(
sidebarLayout(position = "right",
sidebarPanel(
h3("Top Product Analysis"),
selectInput("country", label = "Select Country:",
choices = unique(retail_clean$country),
selected = unique(retail_clean$country)[1]),
uiOutput("select_category")
),
mainPanel(tableOutput("top_product"))
)
)
From the code above, we will make the first selecInput country
and then uiOutput select_category
below it. Next to it will be the table of the top purchased product. Below is the code for the server side:
# SERVER
output$select_category <- renderUI({
selectInput("category_id", label = "Select Category ID:",
# the choices below is filtered based on input$country
choices = retail_clean %>%
filter(country == input$country) %>%
pull(category_id) %>% unique()
)
})
output$top_product <- renderTable({
# standard data wrangling
retail_clean %>%
filter(country == input$country,
category_id == input$category_id) %>%
group_by(description) %>%
summarize(quantity = sum(quantity),
sales = sum(sales),
stock_code = unique(stock_code)) %>%
arrange(desc(quantity)) %>%
rename(Product = description,
'Quantity Purchased' = quantity,
Sales = sales,
'Stock Code' = stock_code) %>%
head(10)
})
Below is the reactive UI we have created:
And with that we have finally finished our journey to understand more about the reactivity feature of Shiny. Full code of this app can be found in the GitHub Page. Hopefully this article will help you further utilize Shiny and have a delightful time with it!
Share this post
Twitter
Facebook
LinkedIn