Create Sparkline Charts with d3

Updated Feb 12, 2018

Sparklines are small, data-dense charts popularized and named by Edward Tufte. The charts are useful for quickly conveying directional trends or frequencies of positive and negative events rather than specific values.

Sparklines are typically compact enough to be displayed inline with text.

They are also useful when presented in groups to compare two or more data sets:

Sparklines are easily created with d3. There’s nothing really special about them compared to other d3 charts except sizing and some concerns about resolution.

Simple bar chart

First we’ll create a simple bar chart:

Simple bar chart Copy
import {
  range       as d3_range,
  select      as d3_select,
  scaleLinear as d3_scaleLinear
from 'd3';
const WIDTH      = 160;
const HEIGHT     = 30;
const DATA_COUNT = 40;
const BAR_WIDTH  = (WIDTH - DATA_COUNT) / DATA_COUNT;
const data = d3_range(DATA_COUNT).map( d => Math.random() );
const x    = d3_scaleLinear().domain([0, DATA_COUNT]).range([0, WIDTH]);
const y    = d3_scaleLinear().domain([01]).range([HEIGHT, 0]);
const svg = d3_select('#sparklines').append('svg')
  .attr('width', WIDTH)
  .attr('height', HEIGHT)
  .append('g');
svg.selectAll('.bar').data(data)
  .enter()
  .append('rect')
    .attr('class''bar')
    .attr('x', (d, i) => x(i))
    .attr('y', d => HEIGHT - y(d))
    .attr('width', BAR_WIDTH)
    .attr('height', d => y(d))
    .attr('fill''MediumSeaGreen');

Bar chart with negative values

Creating a bar chart with positive and negative values is slightly more involved:

Bar chart with negative values Copy
import {
  range       as d3_range,
  select      as d3_select,
  scaleLinear as d3_scaleLinear
from 'd3';
const WIDTH      = 200;
const HEIGHT     = 30;
const DATA_MAX   = 50;
const DATA_MIN   = -50;
const DATA_COUNT = 40;
const BAR_WIDTH  = (WIDTH - DATA_COUNT) / DATA_COUNT;
const data = d3_range(DATA_COUNT).map( d => DATA_MIN + (DATA_MAX - DATA_MIN) * Math.random() );
const x    = d3_scaleLinear().domain([0, DATA_COUNT]).range([0, WIDTH]);
const y    = d3_scaleLinear().domain([DATA_MIN, DATA_MAX]).range([HEIGHT, 0]);
const svg = d3_select('#sparklines').append('svg')
  .attr('width', WIDTH)
  .attr('height', HEIGHT)
  .append('g');
svg.selectAll('.bar').data(data)
  .enter()
  .append('rect')
    .attr('class''bar')
    .attr('x', (d, i) => x(i))
    .attr('y', d => d > 0 ? y(d) : y(0))
    .attr('width', BAR_WIDTH)
    .attr('height', d => Math.abs(y(d) - y(0)))
    .attr('fill', d => d > 0 ? 'steelblue' : 'tomato');

Line chart

Lastly we have a line chart:

Line chart Copy
import {
  line        as d3_line,
  range       as d3_range,
  select      as d3_select,
  scaleLinear as d3_scaleLinear
from 'd3';
const WIDTH        = 200;
const HEIGHT       = 30;
const MARGIN       = { top: 5, right: 5, bottom: 5, left: 5 };
const INNER_WIDTH  = WIDTH - MARGIN.left - MARGIN.right;
const INNER_HEIGHT = HEIGHT - MARGIN.top - MARGIN.bottom;
const DATA_COUNT   = 40;
const data = d3_range(DATA_COUNT).map( d => Math.random() );
const x    = d3_scaleLinear().domain([0, DATA_COUNT]).range([0, INNER_WIDTH]);
const y    = d3_scaleLinear().domain([01]).range([INNER_HEIGHT, 0]);
const svg = d3_select('#sparklines').append('svg')
  .attr('width', WIDTH)
  .attr('height', HEIGHT)
  .append('g')
    .attr('transform''translate(' + MARGIN.left + ',' + MARGIN.top + ')');
const line = d3_line()
  .x((d, i) => x(i))
  .y(d => y(d));
svg.append('path').datum(data)
  .attr('fill''none')
  .attr('stroke''#bbb')
  .attr('stroke-width'1)
  .attr('d', line);
svg.append('circle')
  .attr('r'2)
  .attr('cx'x(0))
  .attr('cy'y(data[0]))
  .attr('fill''steelblue');
svg.append('circle')
  .attr('r'2)
  .attr('cx'x(DATA_COUNT - 1))
  .attr('cy'y(data[DATA_COUNT - 1]))
  .attr('fill''tomato');

Again a straightforward d3 implementation, this time using a line generator as seen on lines 25-27. The circles are a little fluff to help frame the chart but could probably be left out.

Comments

No comments exist. Be the first!

Leave a comment