Simple Reactable Timeline in R
- reactable R timeline
In this post, I will demonstrate how to create the below timeline using reactable.
Motivation
I’ve wanted to create an interactive timeline for awhile. There are a lot of great R timeline packages out there, but none of them really fit my needs.
I’m a huge fan of the reactable package, mainly for its sleek look and customizability. As a result, I had already invested a lot of time integrating “reactables” into my reports. Rather than annihilating my work hitherto, I needed a solution to augment my existing reactables with a graphical timeline.
The beauty of this solution is that it preserves the tabular framework of reactable.
First, let’s load the data.
sales <- readr::read_csv("https://raw.githubusercontent.com/sccmckenzie/reactable-timeline/master/sales.csv")
This is a simple dataset that I created for demonstration. It contains store records of customer revenue.
- c_id: Unique customer ID
- revenue: Total revenue from items purchased by customer
- enter: Time when customer enters store
- exit: Time when customer exits store
sales
## # A tibble: 10 x 4
## c_id revenue enter exit
## <dbl> <dbl> <time> <time>
## 1 1 23.3 18:41 19:44
## 2 2 21.4 08:55 09:49
## 3 3 44.9 17:37 19:07
## 4 4 57.2 11:37 13:28
## 5 5 27.6 10:27 11:28
## 6 6 21.5 12:25 13:20
## 7 7 50.8 08:48 10:29
## 8 8 28.9 08:02 09:00
## 9 9 0.610 09:30 10:02
## 10 10 5.44 10:07 10:30
Our goal is to project enter
and exit
into a “timeline column”.
Step 0: Libraries
This solution requires the below packages:
library(tidyverse)
library(lubridate) # needed for time_length()
library(htmltools) # needed for div()
library(reactable)
Step 1: Define time transform function
convert_timestamps
will take enter
and exit
, normalize, merge, then return as list
.
convert_timestamps <- function(t1, t2) {
# extract timestamp range
t01 <- min(t1)
t02 <- max(t2)
# normalize event timestamps within total range
left <- time_length(t1 - t01)/time_length(t02 - t01)
width <- time_length(t2 - t1)/time_length(t02 - t01)
# splice values into list
out <- list()
for (i in 1:length(left)) {
out[[i]] <- list(left[i], width[i])
}
out
}
Example:
sales %>%
mutate(timeline = convert_timestamps(enter, exit))
## # A tibble: 10 x 5
## c_id revenue enter exit timeline
## <dbl> <dbl> <time> <time> <list>
## 1 1 23.3 18:41 19:44 <list [2]>
## 2 2 21.4 08:55 09:49 <list [2]>
## 3 3 44.9 17:37 19:07 <list [2]>
## 4 4 57.2 11:37 13:28 <list [2]>
## 5 5 27.6 10:27 11:28 <list [2]>
## 6 6 21.5 12:25 13:20 <list [2]>
## 7 7 50.8 08:48 10:29 <list [2]>
## 8 8 28.9 08:02 09:00 <list [2]>
## 9 9 0.610 09:30 10:02 <list [2]>
## 10 10 5.44 10:07 10:30 <list [2]>
Step 2: Define html helper
reactable()
will feed output from convert_timestamps
into create_timeline_bar
, producing our final output.
create_timeline_bar <- function(left = 0, width = "100%", fill = "#00bfc4") {
left <- scales::percent(left)
width <- scales::percent(width)
bar <- div(style = list(
position = "absolute",
left = left,
background = fill,
width = width,
height = "140%")
)
chart <- div(style = list(
flexGrow = 1,
position = "relative",
display = "flex",
alignItems = "center",
height = "100%"
),
bar)
div(style = list(
height = "100%"
),
chart)
}
Final product
sales %>%
mutate(timeline = convert_timestamps(enter, exit)) %>%
# ^ created in Step 1
reactable(fullWidth = FALSE,
compact = TRUE,
bordered = TRUE,
columns = list(
c_id = colDef(width = 55),
revenue = colDef(width = 90, show = FALSE, format = colFormat(digits = 1)),
enter = colDef(width = 80),
exit = colDef(width = 80),
timeline = colDef(
width = 90,
cell = function(value) {
create_timeline_bar(left = value[[1]], width = value[[2]])
# ^ created in Step 2
}
)
)
)