Adding Subset Buttons to plotly Plots
Motivation
I recently found myself drafting some plotly
figures for work, and I wanted to add a bit more interactivity without creating a shiny
app. I plotted time-series data and I wanted the end user to be able to subset the plot to a year of their choosing, or to see the entire range of time in one figure.
After a bit of googling around, I found a few solutions, but this one suited my needs the best. These buttons are straight-forward for the user and pretty simple to code up. Below is a quick demo.
Data Preparation
library(tidyverse)
library(plotly)
We’ll be using Covid-19 case data in the United States, downloaded from Our World in Data.
raw_data <-
read.csv("./data/owid-covid-data.csv", header = TRUE)
head(raw_data)
## iso_code continent location date total_cases new_cases
## 1 AFG Asia Afghanistan 2020-02-24 5 5
## 2 AFG Asia Afghanistan 2020-02-25 5 0
## 3 AFG Asia Afghanistan 2020-02-26 5 0
## 4 AFG Asia Afghanistan 2020-02-27 5 0
## 5 AFG Asia Afghanistan 2020-02-28 5 0
## 6 AFG Asia Afghanistan 2020-02-29 5 0
## new_cases_smoothed total_deaths new_deaths new_deaths_smoothed
## 1 NA NA NA NA
## 2 NA NA NA NA
## 3 NA NA NA NA
## 4 NA NA NA NA
## 5 NA NA NA NA
## 6 0.714 NA NA NA
## total_cases_per_million new_cases_per_million new_cases_smoothed_per_million
## 1 0.125 0.125 NA
## 2 0.125 0.000 NA
## 3 0.125 0.000 NA
## 4 0.125 0.000 NA
## 5 0.125 0.000 NA
## 6 0.125 0.000 0.018
## total_deaths_per_million new_deaths_per_million
## 1 NA NA
## 2 NA NA
## 3 NA NA
## 4 NA NA
## 5 NA NA
## 6 NA NA
## new_deaths_smoothed_per_million reproduction_rate icu_patients
## 1 NA NA NA
## 2 NA NA NA
## 3 NA NA NA
## 4 NA NA NA
## 5 NA NA NA
## 6 NA NA NA
## icu_patients_per_million hosp_patients hosp_patients_per_million
## 1 NA NA NA
## 2 NA NA NA
## 3 NA NA NA
## 4 NA NA NA
## 5 NA NA NA
## 6 NA NA NA
## weekly_icu_admissions weekly_icu_admissions_per_million
## 1 NA NA
## 2 NA NA
## 3 NA NA
## 4 NA NA
## 5 NA NA
## 6 NA NA
## weekly_hosp_admissions weekly_hosp_admissions_per_million total_tests
## 1 NA NA NA
## 2 NA NA NA
## 3 NA NA NA
## 4 NA NA NA
## 5 NA NA NA
## 6 NA NA NA
## new_tests total_tests_per_thousand new_tests_per_thousand new_tests_smoothed
## 1 NA NA NA NA
## 2 NA NA NA NA
## 3 NA NA NA NA
## 4 NA NA NA NA
## 5 NA NA NA NA
## 6 NA NA NA NA
## new_tests_smoothed_per_thousand positive_rate tests_per_case tests_units
## 1 NA NA NA
## 2 NA NA NA
## 3 NA NA NA
## 4 NA NA NA
## 5 NA NA NA
## 6 NA NA NA
## total_vaccinations people_vaccinated people_fully_vaccinated total_boosters
## 1 NA NA NA NA
## 2 NA NA NA NA
## 3 NA NA NA NA
## 4 NA NA NA NA
## 5 NA NA NA NA
## 6 NA NA NA NA
## new_vaccinations new_vaccinations_smoothed total_vaccinations_per_hundred
## 1 NA NA NA
## 2 NA NA NA
## 3 NA NA NA
## 4 NA NA NA
## 5 NA NA NA
## 6 NA NA NA
## people_vaccinated_per_hundred people_fully_vaccinated_per_hundred
## 1 NA NA
## 2 NA NA
## 3 NA NA
## 4 NA NA
## 5 NA NA
## 6 NA NA
## total_boosters_per_hundred new_vaccinations_smoothed_per_million
## 1 NA NA
## 2 NA NA
## 3 NA NA
## 4 NA NA
## 5 NA NA
## 6 NA NA
## new_people_vaccinated_smoothed new_people_vaccinated_smoothed_per_hundred
## 1 NA NA
## 2 NA NA
## 3 NA NA
## 4 NA NA
## 5 NA NA
## 6 NA NA
## stringency_index population population_density median_age aged_65_older
## 1 8.33 40099462 54.422 18.6 2.581
## 2 8.33 40099462 54.422 18.6 2.581
## 3 8.33 40099462 54.422 18.6 2.581
## 4 8.33 40099462 54.422 18.6 2.581
## 5 8.33 40099462 54.422 18.6 2.581
## 6 8.33 40099462 54.422 18.6 2.581
## aged_70_older gdp_per_capita extreme_poverty cardiovasc_death_rate
## 1 1.337 1803.987 NA 597.029
## 2 1.337 1803.987 NA 597.029
## 3 1.337 1803.987 NA 597.029
## 4 1.337 1803.987 NA 597.029
## 5 1.337 1803.987 NA 597.029
## 6 1.337 1803.987 NA 597.029
## diabetes_prevalence female_smokers male_smokers handwashing_facilities
## 1 9.59 NA NA 37.746
## 2 9.59 NA NA 37.746
## 3 9.59 NA NA 37.746
## 4 9.59 NA NA 37.746
## 5 9.59 NA NA 37.746
## 6 9.59 NA NA 37.746
## hospital_beds_per_thousand life_expectancy human_development_index
## 1 0.5 64.83 0.511
## 2 0.5 64.83 0.511
## 3 0.5 64.83 0.511
## 4 0.5 64.83 0.511
## 5 0.5 64.83 0.511
## 6 0.5 64.83 0.511
## excess_mortality_cumulative_absolute excess_mortality_cumulative
## 1 NA NA
## 2 NA NA
## 3 NA NA
## 4 NA NA
## 5 NA NA
## 6 NA NA
## excess_mortality excess_mortality_cumulative_per_million
## 1 NA NA
## 2 NA NA
## 3 NA NA
## 4 NA NA
## 5 NA NA
## 6 NA NA
Next, we’ll limit to United States data points, and select only the necessary columns. We’ll use new_cases_smoothed
(smoothed over the week of data) to avoid the noise of daily fluctuation around the line. We’ll also fix the variable class of date
.
us_df <-
raw_data %>%
select(location, date, new_cases_smoothed) %>%
filter(location == "United States") %>%
mutate(date = as.POSIXct(date))
us_df %>%
filter(!is.na(new_cases_smoothed)) %>%
head()
## location date new_cases_smoothed
## 1 United States 2020-01-28 0.571
## 2 United States 2020-01-29 0.714
## 3 United States 2020-01-30 0.714
## 4 United States 2020-01-31 0.857
## 5 United States 2020-02-01 0.857
## 6 United States 2020-02-02 0.429
# huge file, remove to save memory
rm(raw_data)
Analysis
We will start by plotting a regular plotly
figure, as a “before” shot, if you will.
regular_plot <-
us_df %>%
plot_ly(x = ~date) %>%
add_lines(y = ~new_cases_smoothed) %>%
layout(title = "Covid-19 Incidence in the US",
yaxis = list(title = "New Cases (Smoothed)"))
regular_plot
Now, we’ll apply the buttons. To do this, we will use an updatemenu relayout
method, as detailed here. There are several methods, but relayout
is the one most appropriate, since it will allow the user to modify a layout attribute, in this case the range of the x-axis.
button_plot <-
us_df %>%
plot_ly(x = ~date) %>%
add_lines(y = ~new_cases_smoothed) %>%
layout(
title = "Covid-19 Incidence in the US",
yaxis = list(title = "New Cases (Smoothed)"),
updatemenus = list(
list(type = "buttons",
buttons = list(list(label = "All",
method = "relayout",
args = list(list(xaxis = list(range = c("2020-01-01","2022-12-31"))))),
list(label = "2020",
method = "relayout",
args = list(list(xaxis = list(range = c("2020-01-01","2020-12-31"))))),
list(label = "2021",
method = "relayout",
args = list(list(xaxis = list(range = c("2021-01-01","2021-12-31"))))),
list(label = "2022",
method = "relayout",
args = list(list(xaxis = list(range = c("2022-01-01","2022-12-31")))))
))))
button_plot
Conclusion
And that’s it! We took a plotly
figure and made it even more functional with a few easy-to-code controls. Just when I thought I knew the ins and outs of plotly
, it surprised me with a sweet feature I’ve never seen.