Data Clock in QGIS and Plotly

Visualization of seasonal or cyclic time series data as a circular, polar heatmap

I found the data clock in ArcGIS Pro quite nice to visualize seasonal patterns in time series. The rings of the chart show the larger, cyclic time unit (e.g. year), while each ring is divided into smaller units shown as wedges. Of course, I wanted to be able to do the same using open source tools, and it was possible in Python using Pandas and Plotly.


Example of a Data Clock chart. Data: Piracy incidents, Maritime Safety Information portal

Data Clock in QGIS

With my QGIS Plugin Data Clock, the chart can be generated right from the processing toolbox, with any vector layer with at least one Date or DateTime field. It is also possible to call the figure factory functions from the QGIS Python Console. The data is binned into these rings and wedges and the color is determined by the count of data rows or by an aggregation function (e.g. ‘sum’, ‘mean’, ‘median’) on a specified numerical column. The following combinations of rings and wedges are implemented: Year-Month, Year-Week, Year-Day, Week-Day, Day-Hour. The result is a HTML file with an interactive Plotly chart (including a tooltip on hover).

The plugin is in the QGIS plugin registry and can be installed in QGIS with Plugins – Manage and Install Plugins. “Show also Experimental Plugins” must be checked in the settings. Requirements are Plotly and Pandas.

Data Clock in Python

If you are looking for this kind of chart in Python with Plotly and Pandas, take a look at my Jupyter Notebook.

Using Plotly from the QGIS Python Console

It is pretty easy to use Plotly for any kind of charts of your vector layer attributes, right from the QGIS Python Console. Note that the high level functions of Plotly Express expect data in a Pandas DataFrame, therefore it is better to use the Plotly Graph Objects directly. The following code pulls the data of two fields using a list comprehension, creates a scatter plot figure, and fig.show() opens the chart in a browser. This way you have access to Plotly methods such as update_layout(), update_traces() or to_html().

import numpy as np
import plotly.graph_objects as go

layer = iface.activeLayer()

x_field = 'dateofocc'
y_field = 'victim_l'

x = [f[x_field] for f in layer.getFeatures()] 
y = [f[y_field] for f in layer.getFeatures()] 

fig = go.Figure(
    layout_title_text="Title"
)

fig.add_trace(go.Scatter(
    x=x,
    y=y,
    mode='markers'
))
            
fig.show()