Fork me on GitHub

This tutorial demonstrates how to create interactive timelines (or Gantt charts) like the one below using a variety of different libraries, currently including; ggplot2 and plotly. Note that the googleVis library is capable of generating timelines but these depend on Flash and are therefore not covered in this tutorial.

An interactive shiny application containing a plotly chart is available here: https://livedataoxford.shinyapps.io/htmlwidget_template_timeline/.

The datasets that this tutorial considers are structured as follows, i.e. events have a duration and are not instantaneous.

Start.Date End.Date Timeline.Label Timeline.Tooltip.Info Event.Category
1789-03-29 1797-02-03 Thing A Label 1 X
1797-02-03 1801-02-03 Thing B Label 2 X
1801-02-03 1809-02-03 Thing A Label 2 Y

Where the “Start Date” and “End Date” column contain the start and end dates for the events, which must be formatted as YYYY-MM-DD. If your dates are not formatted like this, then refer to the “Date Manipulations Script” provided separartely. The “Timeline Label” and “Timeline Tooltip Info” columns provide information about how the events should be labelled in the timeline and what should be shown in the tooltip, respectively. Finally, the “Event Category” column is used for colour coding events.

Note that this template covers both how to build gantt charts inside of an HTML RMarkdown file and how to functionalise the code so as to conveniently switch between different categories and metrics in a Shiny app.

Import Data

The data for this template is a .csv file accessed from Figshare here using read.csv. The .csv file contains a list of the UK Prime Ministers from January 1908 to June 2016.

timeline_data <- read.csv(file="https://ndownloader.figshare.com/files/5533448", stringsAsFactors = FALSE)
library(lubridate)
timeline_data$Start.Date <- dmy(timeline_data$Start.Date)
timeline_data$End.Date <- dmy(timeline_data$End.Date)
knitr::kable(timeline_data[1:4,])
Prime.Minister Start.Date End.Date Political.Party
David Cameron 2010-05-11 NA Conservatives
Gordon Brown 2007-06-27 2010-05-11 Labour
Tony Blair 1997-05-02 2007-06-27 Labour
John Major 1990-11-28 1997-05-02 Conservatives

An advanced version of this template might attempt to automatically infer the label, tooltip and event categorisation columns but in this case we explicitly state the label and categorisation column.

label_column <- "Prime.Minister"
category_column <- "Political.Party"

The tooltip information will be generated from the label and category columns in this example.

Data Processing

An individual may sit as Prime Mininster of the United Kingdom an unlimited nmber of times, in this dataset there are several indivduals who occupy this office multiple times. This raises an issue in sorting the data such that rows of the Gantt chart are appropriately ordered.

The ave function allows the aggregation of data according to a function, we wish to order according to min - the earliest date an individual became a Prime Minister. Create a data.frame where the prime ministers are ordered according to their earliest starting date, note that the data.frame only contains the earliest sitting of the prime ministers.

earliest_date_by_Prime_Minister <-
  timeline_data[timeline_data$Start.Date == ave(timeline_data$Start.Date, timeline_data$Prime.Minister, FUN =
                                                  min), ]

Re-order the rows of the data.frame by $Start.Date and then by $Prime.Minister

earliest_date_by_Prime_Minister <-
  earliest_date_by_Prime_Minister[order(
   earliest_date_by_Prime_Minister$Start.Date,
    earliest_date_by_Prime_Minister$Prime.Minister), ]

Modify the levels of the factor $Prime.Minister in the timeline_data object according to the order of Prime Minister’s names in the earliest_date_by_Prime_Minister object:

timeline_data$Prime.Minister <-
  factor(timeline_data$Prime.Minister, levels = rev(as.character(unique(earliest_date_by_Prime_Minister$Prime.Minister))))
timeline_data$Political.Party <- as.factor(timeline_data$Political.Party)

Finally, at the time the data was compiled the end of the currently sitting Prime Minister’s reign was unknown - it is recorded as NA. This needs to be removed from the data as the visualisation libraries do not easily support infinitely long bars. Note the use of droplevels to remove empty levels from the factors in timeline_data.

timeline_data <- timeline_data[!is.na(timeline_data$End.Date) & !is.na(timeline_data$Start.Date),]
timeline_data <- droplevels(timeline_data)

For beautification purposes, the colours of the political parties will be used in the visualisation:

party_colours <- list("Labour" = "#DC241f", "Conservatives" = "#0087DC", "Liberal Democrat" = "#FDBB30")
## Order by the levels in timeline_data
party_colours <- as.character(party_colours[levels(timeline_data$Political.Party)])

ggplot timeline

The ggplot library provides an extensive graphics language that allows the creation of a huge variety of different visualisations, from comparative charts to time series plots - including timelines. Note, however, that ggplot output is not dynamic - tooltips are not directly available from the library,

In the code below a ggplot object is instantiated, with the following coordinate system (note that eval(as.name(label_column)) is used to convert strings into names):

  • X Axis: Dates, with the Start.Date and End.Date columns defining the start and end of event series.
  • Y Axis: The label_column - the Prime.Minister name.
  • Color: The category_column - the Political.Party the Prime Minister is associated with.
library(ggplot2)
ggplot_timeline <- ggplot(
  data = timeline_data,
  aes(
    x = Start.Date,
    xend = End.Date,
    y = eval(as.name(label_column)),
    yend = eval(as.name(label_column)),
    colour = eval(as.name(category_column))
  )
)
ggplot_timeline

An empty plot is displayed because only the coordinate system has been defined - to add horizontal bars to the plot we must add a layer to the plot containing geom_segment, two additonal layers with the axes coordinates are added using xlab and ylab:

ggplot_timeline + geom_segment(size=3) + xlab("Date") + ylab("Prime Minister")

The party colour scheme can now be applied:

ggplot_timeline + geom_segment(size=3) + xlab("Date") + ylab("Prime Minister") + scale_colour_manual(name = "Political Parties",values = party_colours)

plotly

The plotly library can directly convert many ggplot objects into interactive charts using ggplotly:

library(plotly)
ggplotly(ggplot_timeline + geom_segment(size=3) + xlab("Date") + ylab("Prime Minister") + scale_colour_manual(name = "Political Parties",values = party_colours))

The tooltip content can be restricted to contain just the y or x coordinate as follows:

ggplotly(ggplot_timeline + geom_segment(size=3) + xlab("Date") + ylab("Prime Minister") + scale_colour_manual(name = "Political Parties",values = party_colours), tooltip = "x")

The documentation for the ggplotly function gives a fairly opaque description of how to populate the tooltip with custom text, “a character vector specifying… the unofficial”text" aesthetic“. This character vector must be supplied as an aesthetic to the ggplot object, i.e. in the aes function:

ggplotly(
  ggplot(
  data = timeline_data,
  aes(
    x = Start.Date,
    xend = End.Date,
    y = eval(as.name(label_column)),
    yend = eval(as.name(label_column)),
    colour = eval(as.name(category_column)),
    text = letters[1:nrow(timeline_data)]
  )
) + geom_segment(size = 3) + xlab("Date") + ylab("Prime Minister") +
  scale_colour_manual(name = "Political Parties",values = party_colours),
  tooltip = "text"
)

Below we define a gantt_labeller function that will format our tooltip content:

gantt_labeler <- function(start_date = NA, end_date = NA, y_axis = NA, color = NA){
  paste0(
    "Prime Minister: ", y_axis, "</br>",
    "Date Range: ", start_date," to ",end_date,
    "</br>",
    "Political Party: ",color
  )
}

Our interactive ggplot now contains an informative and nicely formatted tooltip:

ggplotly(
  ggplot(
  data = timeline_data,
  aes(
    x = Start.Date,
    xend = End.Date,
    y = eval(as.name(label_column)),
    yend = eval(as.name(label_column)),
    colour = eval(as.name(category_column)),
    text = gantt_labeler(start_date = Start.Date, end_date = End.Date, y_axis = eval(as.name(label_column)), color = eval(as.name(category_column)))
  )
) + geom_segment(size = 3) + xlab("Date") + ylab("Prime Minister") + scale_colour_manual(name = "Political Parties",values = party_colours),
  tooltip = "text"
)

timevis

The timevis library uses the vis.js library to build impressive interactive timelines and and gantt charts, with good support for both instantaneous and non-instantaneous event series. Documentation for the library is available [https://github.com/daattali/timevis] and an interactive guide including advanced shiny functionality has also been published by the developer.

Like many libraries, the workhorse of the library (timevis) expects a specially formatted data.frame. Note that at the time of writing (July 2016) the library does not support tooltips.

  • id: Unique for each row
  • content: Gantt label
  • start: start date for each event
  • end: end date for each event
  • title: hover text

Let us create a version of timeline_data with this format and provide this directly to timevis for visualisation; note that there is not a seperate line for each data with this formulation - this requires groups, as discussed below.

library(timevis)
timevis_data <- data.frame(
  id = 1:nrow(timeline_data),
  content = timeline_data[,label_column],
  start = timeline_data$Start.Date,
  end   = timeline_data$End.Date,
  type = c(rep("range", nrow(timeline_data))),
  title = as.character(timeline_data[,category_column])
)
timevis(timevis_data)

Groups

The timevis library allows a “y-axis” to be added to timelines so that events may be grouped together through the groups argument, which requires another specially formatted data.frame. As in this dataset there are multiple items within a single grouping (i.e. Prime Ministers were elected for multiple, separate parliaments) the levels of the column timeline_data$Prime.Minister are used:

timeline_groups <- data.frame(
  id = levels(timeline_data[,label_column]),
  content = levels(timeline_data[,label_column])
)

Adding the column group to the timevis_data object and providing groups = timeline_groups creates an interactive gantt chart, note that at present it is not easy to provide custom colours to the events based on the political party.

timevis_data$group <- timeline_data[,label_column]
timevis(data = timevis_data, groups = timeline_groups, options = list(editable = FALSE),showZoom = T,fit = TRUE,height = "800px")

Shiny App

A shiny app containing an interactive version of the charts above is available here: https://livedataoxford.shinyapps.io/htmlwidget_template_timeline/.

The following types of interaction are supported:

  • Hover over items for tooltips
  • Click items for an interactive table to be displayed below the timeline
  • Drag a rectangular area over the chart to zoom in (double-click to zoom out)
  • Further controls are shown in the top right of the visualisation when your cursor enters the chart