Raspberry Pi Computing

Wednesday, 2 January 2013

Smoothing out the lines in d3.js


When you draw a line graph, what you're doing is taking two (or more) sets of coordinates and connecting them with a line (or lines). I know that sounds simplistic, but bear with me. When you connect these points, you're telling the viewer of the graph that in between the individual points, you expect the value to vary in keeping with the points that the line passes through. So in a way, you're trying to interpret the change in values that are not shown.

Now this is not strictly true for all graph types, but it does hold for a lot of line graphs.
So... when connecting these known coordinated together, you want to make the best estimate of how the values would be represented. In this respect, sometimes a straight line between points is not the best representation.

For instance. Earlier, when demonstrating the extent function for graphing we showed a graph of the varying values with the y axis showing a narrow range.
The resulting variation of the graph shows a fair amount of extremes and you could be forgiven for thinking that if this represented a smoothly flowing analog system of some kind then some of those sharp peaks and troughs would not be a true representation of how the system or figures varied.

So how should it look? Ahh... The $64,000 question. I don't know :-). You will have a better idea since you are the person who will know your data best. However, what I do know is that D3 has some tricks up its sleeve to help.

We can easily change what we see above into;
How about that? And the massive amount of code required to carry out what must be a ridiculously difficult set of calculations?
Now, that is slightly unfair because that's the code that YOU need to put in your script, but Mike Bostock probably had to do the mental equivalent of walking across hot coals to get it to work so nicely.

So where does this neat piece of code go? Here;
var valueline = d3.svg.line()
    .interpolate("basis")           // <=== THERE IT IS!
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });

So is that it? Nooooo........ There's more! This is one form of interpolation effect that can be applied to your data, but there is a range and depending on your data you can select the one that is appropriate.

Here's the list of available options and for more about them head on over to the D3 wiki and look for 'line.interpolate'.
  • linear – Normal line (jagged).
  • step-before – a stepping graph alternating between vertical and horizontal segments.
  • step-after - a stepping graph alternating between horizontal and vertical segments.
  • basis - a B-spline, with control point duplication on the ends (that's the one above).
  • basis-open - an open B-spline; may not intersect the start or end.
  • basis-closed - a closed B-spline, with the start and the end closed in a loop.
  • bundle - equivalent to basis, except a separate tension parameter is used to straighten the spline. This could be really cool with varying tension.
  • cardinal - a Cardinal spline, with control point duplication on the ends. It looks slightly more 'jagged' than basis.
  • cardinal-open - an open Cardinal spline; may not intersect the start or end, but will intersect other control points. So kind of shorter than 'cardinal'.
  • cardinal-closed - a closed Cardinal spline, looped back on itself.
  • monotone - cubic interpolation that makes the graph only slightly smoother.
Because in the course of writing this I took an opportunity to play with each of them, I was pleasantly surprised to see some of the effects and it seems like a shame to deprive the reader of the same joy :-). So at the risk of deforesting the planet (so I hope you are reading this in electronic format) here is each of the above interpolation types applied to the same data.

This is also an opportunity to add some reader feedback awesomeness. Many thanks to 'enjalot' for the great suggestion (in the comments below) to plot the points of the data as separate circles on the graphs. Since the process of interpolation has the effect of 'interpreting' the trends of the data to the extent that in some cases, the lines don't intersect the actual data much at all.

Each of the following shows the smoothing curve and the data that is used to plot the graph.










So, over to you to decide which format of interpolation is going to suit your data best:-).



  1. Thank you - exactly what I was looking for explained clearly and succinctly

    1. Great to hear. I think that everyone has a different way that they prefer to learn information, and when you find a good 'fit' it just makes it easier :-) Good luck with D3.

  2. Hey, this is great!
    It would be really helpful if you also plotted the points of the data as circles so that it was readily apparent that some of these interpolations do not accurately represent the data.
    thanks again!

    1. Wow! That is such a great idea! I will do it! Cheers

    2. Cool! Done. I figured you wouldn't mind me giving you credit by name (enjalot) in the text. If you'd rather not, just let me know. I've also updated the book. (https://leanpub.com/D3-Tips-and-Tricks). Many thanks. Great suggestion. I don't think I've seen this illustrated this way before.

  3. Is it also possible to combine the methods?

    1. Ahh... Interesting question.
      Yes it is.
      All you need to do is add in an additional block like the 'var valueline = blah bal blah...' but name it something different (like valueline2) and then add another block that adds the valueline2 path. That way you have two different paths and you can change the interpolations on either to suit.
      Thanks for the question

  4. Thank you !! :) very very thank you.

  5. thank you .. this is exact what i need .. :)

  6. Nice explanation, but your step-before and step-after examples are inversed.

    1. Thanks. But I don't quite get what you mean. The captions on the graphs above are correct and they appear to be in the same order as the description. I just ran a quick test to make sure that the steps actually do go in the direction that I thought they did so I'm a bit flummoxed. Could you describe the problem in a slightly different way? Thanks for the feedback.

  7. How did you managed to get circles aligned with curved line?

    1. I only have them aligned for those interpolation types that have the lives running directly through the data points. So I'm afraid that it just happens that way depending on the interpolation type.

  8. Thanks for explanation.
    Can I adjust line chart by holding the curve.

    1. Not for the example as shown here. What you would be referring to would be a level of interactivness that would be pretty advanced (I haven't tried it before sorry)

  9. Thanks! `interpolate: 'monotone'` is awesome!

  10. Hello, i would like to ask that how can i find local max and local min values and also if there is a plateau ? I have a graph to measure tension according to values. I have to check for bumps if exist ? Thank you for your interest.

    1. Good question. There will obviously be a bit of work involved here. I assume that we are looking for a way to find areas where the change in gradient means the line that is tangent to the curve changes relative to points before and after it. I have never played with this before, but the following pages may be of use; http://www.thesoftwaresimpleton.com/blog/2016/01/26/tangent/, http://www.d3geometry.com/functions?uid=8888, https://bl.ocks.org/andresin87/4c5a34136c9a543d68de