Creating Time Maps in R
Time Maps are a great way to understand events that happen with a certain cadence over time. Take a look at this article by Mark Watson for an excellent explanation on what they are and when they can be useful.
The end result of this article will be to create a Time Map that looks like this:
Sample Data
The type of data I usually want to put in this type of chart comes in the form <TimeStamp> | <Event>
. Here’s an example data frame:
library(dplyr)
set.seed(123)
df <- data.frame(helper = c(rnorm(500, 120, 100),
rnorm(500, 1, .8))) %>%
mutate(elapsed = cumsum(helper),
date = as.POSIXct('2019-05-25 10:00:00') + elapsed,
event = 'Ping') %>%
select(date, event)
This will create a data frame with events that happen about 2 minutes apart and then 1 second apart. The data frame looks like this (the event is useless, it’s there just to make the data frame more interesting):
> head(df)
date event
1 2019-05-25 10:01:03 Ping
2 2019-05-25 10:02:40 Ping
3 2019-05-25 10:07:16 Ping
4 2019-05-25 10:09:23 Ping
5 2019-05-25 10:11:36 Ping
6 2019-05-25 10:16:28 Ping
Transforming the data
Next step, we need to have a “time before” and “time after” value for our data frame. Here’s how to do it:
before_after <- df %>%
arrange(date) %>%
mutate(before = as.numeric(date - lag(date)),
after = as.numeric(lead(date) - date)) %>%
filter(!is.na(before) & !is.na(after))
This will create the following data frame:
> head(before_after)
date event before after
1 2019-05-25 10:02:40 Ping 96.98225 275.870831
2 2019-05-25 10:07:16 Ping 275.87083 127.050839
3 2019-05-25 10:09:23 Ping 127.05084 132.928774
4 2019-05-25 10:11:36 Ping 132.92877 291.506499
5 2019-05-25 10:16:28 Ping 291.50650 159.585497
6 2019-05-25 10:19:07 Ping 159.58550 6.506124
Plotting the data
The simplest plot would be:
library(ggplot2)
ggplot(before_after, aes(x = before, y = after)) +
geom_hex()
But this is not very informative. There’s a very high concentration near (0,0) that we can barely see. Let’s use a logarithmic scale, and also add a bit more color to the chart:
library(RColorBrewer)
ggplot(before_after, aes(x = before, y = after)) +
geom_hex() +
scale_fill_gradientn(colours = rev(brewer.pal(5, "Spectral"))) +
scale_y_continuous(trans = "log10") +
scale_x_continuous(trans = "log10")
Better, but the axis labels aren’t very informative… Using breaks and labels on the axis, the time intervals become much more clear:
axis_labels = list('1/100s' = 0.1, '1 sec' = 1, '1 min' = 60,
'2 min' = 120, '10 min' = 600)
ggplot(before_after, aes(x = before, y = after)) +
geom_hex() +
scale_fill_gradientn(colours = rev(brewer.pal(5, "Spectral"))) +
scale_y_continuous(trans = "log10", minor_breaks = NULL,
breaks = unlist(axis_labels),
labels = names(axis_labels)) +
scale_x_continuous(trans = "log10", minor_breaks = NULL,
breaks = unlist(axis_labels),
labels = names(axis_labels)) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Here’s the complete code for the chart: