Take the 2-minute tour ×

the pie chart update example on the bl.ocks site doesn't update the elements 'in place':


function change() {
  path = path.data(pie(dataset[this.value])); // update the data
  // set the start and end angles to Math.PI * 2 so we can transition
  // anticlockwise to the actual values later
        return color(i);
      .attr("d", arc(enterAntiClockwise))
      .each(function (d) {
        this._current = {
          data: d.data,
          value: d.value,
          startAngle: enterAntiClockwise.startAngle,
          endAngle: enterAntiClockwise.endAngle
      }); // store the initial values

      .attrTween('d', arcTweenOut)
      .remove() // now remove the exiting arcs

  path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs

Instead, it just treats the new array of value as brand new data and resizes the chart accordingly.

I've created a fiddle demonstrating the issue very simply:


If you press 'add', it add a random int to the array: this works as intended.

If you press 'remove', the only element getting transitioned out is always the last element to have entered the pie. In short, it behaves like a LIFO stack. The expected behaviour is for the relevant pie arc to get transitioned out instead.

Is it possible to apply object consistency to pies? I've also tried adding a key function (not demonstrated on the fiddle) but that just breaks (oddly enough it works fine with my stacked graphs).

Thank you.

share|improve this question
Can you provide a graphic example of what you mean - what would you expect to see before and after adding a new element? It is unclear to me - a pie essentially represents 100% of a data set. When you add a new element to a pie chart, all the other elements have to reduce proportionally, and the converse is also true. –  mccannf May 30 '13 at 22:46
Hi mccannf, thank you for your note. If you look at the second linked jsfiddle in my original post, you can see that when pressing 'remove' you are removing a specific, random data element from the set. I'd expect that particular arc to transition out. Instead, it recolors all arcs with new colors, and transition out the last one that entered - no longer reflecting the underlying dataset. For that reason, it appears d3 is not preserving the indices either. –  Stephan Tual May 30 '13 at 22:53
Ok, understood. Would something like this example help: bl.ocks.org/mbostock/3888852. Here mbostock has assigned a domain of colors to the data headers, and an associated legend as well. –  mccannf May 30 '13 at 23:00
Thank you mccannf. This didn't help me, sadly. I've however updated the fiddle to demonstrate the problem clearly. I've made it console.log() the actual array element from the dataset that's being removed, followed by what D3 is transitioning out on exit(). As you can see, it's different, and i'm scratching my head as to why. –  Stephan Tual May 30 '13 at 23:27
Note: just updated the link, fiddle had created a new one :) –  Stephan Tual May 30 '13 at 23:45
show 1 more comment

2 Answers

up vote 10 down vote accepted

The easiest solution to this problem is to set missing values to zero, rather than removing them entirely, as in Part III of the Pie Chart Update series of examples. Then you get object constancy for free: you have the same number of elements, in the same order, across updates.

Alternatively, if you want a data join as in Part IV, you have to tell D3 where the entering arcs should enter from, and where the exiting arcs should exit to. A reasonable strategy is to find the closest neighboring arc from the opposite data: for a given entering arc, find the closest neighboring arc in the old data (pre-transition); likewise for a given exiting arc, find the closest neighboring arc in the new data (post-transition).

To continue the example, say you’re showing sales of apples in different regions, and want to switch to show oranges. You could use the following key function to maintain object constancy:

function key(d) {
  return d.data.region;

(This assumes you’re using d3.layout.pie, which wraps your original data and exposes it as d.data.)

Now say when you transition to oranges, you have the following old data and new data:

var data0 = path.data(), // retrieve the old data
    data1 = pie(region.values); // compute the new data

For each entering arc at index i (where d is data1[i]), you can step sequentially through preceding data in data1, and see if you can find a match in data0:

var m = data0.length;
while (--i >= 0) {
  var k = key(data1[i]);
  for (var j = 0; j < m; ++j) {
    if (key(data0[j]) === k) return data0[j]; // a match!

If you find a match, your entering arcs can start from the matching arc’s end angle. If you don’t find a preceding match, you can then look for a following matching arc instead. If there are no matches, then there’s no overlap between the two datasets, so you might enter the arcs from angle 0°, or do a crossfade. You can likewise apply this technique to exiting arcs.

Putting it all together, here’s Part V:

pie chart update v

share|improve this answer
Part V, just for me :) Yeah!!!! Thank you so much Mike!!!!!!! –  Stephan Tual May 31 '13 at 0:19
add comment

Ok, found the solution. The trick was to pass the key this way:

path = path.data(pie(dataset), function (d) {return d.data}); // this is good

as opposed to not passing it, or passing it the wrong way:

path = path.data(pie(dataset, function (d) {return d.data})); // this is bad

And here's an updated fiddle with a working transition on the right arc! :)


share|improve this answer
Don’t think that’s right. If your key function is return d.data, then all your keys will be "[object Object]" and bad things will happen. Please see my answer, and I also recommend reading the object constancy tutorial on key functions. –  mbostock May 31 '13 at 0:13
Wow I'm getting schooled by Mbostock himself, it's an honor! Thanks Mike for the detailed answer below, I'll vote you up. –  Stephan Tual May 31 '13 at 0:18
add comment

Your Answer


