SVG-based Sparkline with d3.js

DECEMBER 19, 2013

This is a tutorial of how to create various SVG based sparklines with d3.js. Sparkline, a tiny line chart, is often very effective and visually appealing, especially for today's small screen devices.

Yale University Professor, Edward Tufte, invented sparkline that is often seen in many web sites these days. I've attended one of his courses many years back and been fascinated with the history and depth of information design since. This is his quote about sparkline:

A sparkline is a small intense, simple, word-sized graphic with typographic resolution. Sparklines mean that graphics are no longer cartoonish special occasions with captions and boxes, but rather sparkline graphic can be everywhere a word or number can be: embedded in a sentence, table, headline, map, spreadsheet, graphic.

Edward Tufte

Using d3.js, we can fairly easily draw SVG-based sparklines. This is 2013 historical stock prices for Google $1084.75. And this is for Facebook $55.57. And this is for Apple $550.77. Each sparkline has 244 data points, but it's condensed very nicely.

In this tutorial, I downloaded historical stock prices from Google Finance. You can download CSV files from there. The file contains many extra columns, and I dropped all columns but Date and Close Price. Here is my first attempt of sparkline:


<style>
.sparkline {
    fill: none;
    stroke: #000;
    stroke-width: 0.5px;
}
</style>
...
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 100;
var height = 25;
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var parseDate = d3.time.format("%b %d, %Y").parse;
var line = d3.svg.line()
                 .x(function(d) { return x(d.date); })
                 .y(function(d) { return y(d.close); });

function sparkline(elemId, data) {
    data.forEach(function(d) {
        d.date = parseDate(d.Date);
        d.close = +d.Close;
    });
    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain(d3.extent(data, function(d) { return d.close; }));

    d3.select(elemId)
      .append('svg')
      .attr('width', width)
      .attr('height', height)
      .append('path')
      .datum(data)
      .attr('class', 'sparkline')
      .attr('d', line);
}

d3.csv('/csv/goog.csv', function(error, data) {
    sparkline('#spark_goog', data);
});
...
</script>
                    

The code above produces a sparkline for Google like this, but if you look at it carefully, it looks edgy. This is because I didn't set any interpolation for the line. Let's set a basis interpolation.


var line = d3.svg.line()
                 .interpolate("basis")
                 .x(function(d) { return x(d.date); })
                 .y(function(d) { return y(d.close); });
                    

With basis interpolation, new sparkline looks like this. Do you see it looks a little smoother now? Depending on your data representation, you can set different interpolation modes.

Lastly, let's plot a little red circle at the end of line. You can just add a circle element, but you want to add some margin at top, bottom and right in order to avoid cutting off a circle. Here is the modified code:


<style>
.sparkline {
    fill: none;
    stroke: #000;
    stroke-width: 0.5px;
}
.sparkcircle {
    fill: #f00;
    stroke: none;
}
</style>
...
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 100;
var height = 25;
var x = d3.scale.linear().range([0, width - 2]);
var y = d3.scale.linear().range([height - 4, 0]);
var parseDate = d3.time.format("%b %d, %Y").parse;
var line = d3.svg.line()
                 .interpolate("basis")
                 .x(function(d) { return x(d.date); })
                 .y(function(d) { return y(d.close); });

function sparkline(elemId, data) {
    data.forEach(function(d) {
        d.date = parseDate(d.Date);
        d.close = +d.Close;
    });
    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain(d3.extent(data, function(d) { return d.close; }));

    var svg = d3.select(elemId)
                .append('svg')
                .attr('width', width)
                .attr('height', height)
                .append('g')
                .attr('transform', 'translate(0, 2)');
    svg.append('path')
       .datum(data)
       .attr('class', 'sparkline')
       .attr('d', line);
    svg.append('circle')
       .attr('class', 'sparkcircle')
       .attr('cx', x(data[0].date))
       .attr('cy', y(data[0].close))
       .attr('r', 1.5);  
}

d3.csv('/csv/goog.csv', function(error, data) {
    sparkline('#spark_goog', data);
});
...
</script>
                    

This will produce the first examples you saw at the top, like this $1084.75. If you are interested in what you should or shouldn't do in sparkline, I would recommend you to check out Mr. Tufte's lecture.

Related Post

Animating Pie Chart on iOS in Swift

JUNE 18, 2019

Step-by-step tutorial of how to draw an animating pie chart on iOS app by using Core Animation in Swift.