HomeSoftwareAgencySuccess StoriesAmazon Expertise
ENFR

Data visualization using Highcharts React and Crossfilter

Motivation

At Seelk Tech, our motto is "Amazon Analytics at Your Fingertips", so our SaaS helps our users analyse their data at multiple dimensions — country, brand or custom groupings and to deep dive at the product level to gain insights that really count for their business.

Analysing data implies data visualization, but one does not simply create readable and comprehensive charts with a huge set of data — furthermore when this data can be grouped and/or filtered on different dimensions.

We figured out that the last point already had a solution : crossfilter. It is a powerful tool that we've integrated across our SaaS for data handling to create charts, tables and files.

From the outset, we wanted a powerful tool to create interactive charts, a tool that was complete and well documented and Highcharts is the one that convinced us the most (+ the Highcharts team already released a wrapper for React 🤓)

For the sake of clarity, you will find all the assets needed (Classes and Wrapper that we developed for both Crossfilter and Highcharts) in this repository — you will find :

  • BaseSeries
  • TimeSeries
  • ExtraDimensionsSeries
  • HighchartsHelpers
  • MultiHighchartsHelpers
  • Chart

Crossfilter integration

There's some parameters you have to understand before using a crossfilter object

  • data : the main part of our crossfilter, this is on this data that all the operations will be performed
  • group : when using a crossfilter object and you want to get data from it, you have to specify a group (time parameter, filter)
  • filters : same as group
  • unit : which key you want to do an operation on
  • dimension : what you can use as a grouping/filter

So basically, let's say we have this dataset, we could create and start using our crossfilter like this :

const orders = [
  {
    country_code: 'FR',
    orders_count: 42,
    units_sold: 56,
    total_sales: 200.40,
    date: moment('2019-09-15', 'YYYY-MM-DD'),
  },
  {
    country_code: 'DE',
    orders_count: 88,
    units_sold: 102,
    total_sales: 432.10,
    date: moment('2019-09-15', 'YYYY-MM-DD'),
  },
  {
    country_code: 'FR',
    orders_count: 38,
    units_sold: 50,
    total_sales: 182.20,
    date: moment('2019-09-16', 'YYYY-MM-DD'),
  },
  {
    country_code: 'DE',
    orders_count: 102,
    units_sold: 128,
    total_sales: 620.40,
    date: moment('2019-09-16', 'YYYY-MM-DD'),
  },
  {
    country_code: 'FR',
    orders_count: 40,
    units_sold: 46,
    total_sales: 168.50,
    date: moment('2019-09-17', 'YYYY-MM-DD'),
  },
]

We will now need a DataSeries that we will need if we want to perform the actions we need at Seelk.

class OrdersSeries extends ExtraDimensionSeries(TimeSeries) {
  createDimensions() {
    super.createDimensions()
    this.cf.dimensions.country_code = this.cf.data.dimension(v => v.country_code)
  }

  updateFilters(values = {}) {
    super.updateFilters(values)
    const {country_code = null} = this.filters
    this.filterDimension(this.cf.dimensions.country_code, country_code)
  }
  
  getUnitAttribute(unit) {
    return obj => {
      switch (unit) {
        case 'orders_count':
          return obj.orders_count
        case 'units_sold':
          return obj.units_sold
        case 'total_sales':
          return obj.total_sales
        default:
          return super.getUnitAttribute(unit)(obj)
      }
    }
  }
}

You may now ask yourself what is a DataSeries and what are those ExtraDimensionSeries / TimeSeries classes?

Our Series inherit from BaseSeries which is a wrapper containing all the utils we might need, and ExtraDimensionSeries allows us to use other Series we may use that contain utils — here we're using TimeSeries so we can perform some actions on the key date — and we handle this key using a moment object.

Everytime we have to initialize a crossfilter object, we are doing it using a DataSeries object.

So, creating (and then using) a crossfilter object would be done this way for us:

const ordersCrossfilter = OrdersSeries(orders).init()

With this crossfilter, we can now perform this type of action:

ordersCrossfilter.getData({group: 'country_code', unit: 'orders_count'})
=> [{FR: 120, DE: 190}]

getData is a function from BaseSeries

So it's nice, we've got our crossfilter setup and performing actions using it is easy now! Let's see how to use it to build a chart!

Highcharts React integration

Using our crossfilter object, we are ready to build charts efficiently and quickly!

In order to build them, we will use what we developed at Seelk and what we call HighchartsHelpers.

The main idea behind HighchartsHelpers is that we needed a generic util that can be used everytime we want to build a chart. A chart can be different on many parts, especially on the data part because depending on the chart, the axis might be different and it impacts a lot (i.e. charts with categories as x-axis or charts with datetime as x-axis).

Copyright 2020 © All rights reserved

Seelk New York

5th Avenue

001 938872 0238

Seelk Paris

7 - 11 Boulevard Haussman

0033 1873 378273