# how to move from d3.group to a format that works for a sunburst chart?

**URL:** <https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931>\
**Category:** Help\
**Created:** [September 25, 2020, 3:29am UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931 "2020-09-25T03:29:29Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [September 25, 2020, 3:29am UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931/1 "2020-09-25T03:29:29Z")

</div>

This is a follow-up to my question on [Rendering Mapped / Grouped Data](https://talk.observablehq.com/t/rendering-mapped-grouped-data/3476).

* * *

**EDIT** : I originally thought this question about about recursive renaming of object keys and values, but re-considering it, my question is essentially this: **How do I move from data organized by `d3.group` into a format that I can plug into a Zoomable Sunburst chart?**

* * *

Following @mootari’s advice, I did some reading into [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#), Set and [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) methods. More specifically, I learned what a method really is, and got a crash course on JS fundamentals, which has been very helpful. Thanks Fabian!

However, while I am now more capable in manipulating data, I still haven’t managed to figure out how this would be done _recursively_ - such as when trying to coax grouped data into a format that expected for the Zoomable Sunburst chart.

For example - I pulled some data on projects financed by the Asian Development Bank, and then used d3.group to organize these data first by ‘status’, then ‘country’, then ‘sector’. The code is this:

```auto
grouped_data = d3.group(sovereign_projects.slice(1), d => d["Status"], d => d["Country"], d => d["Sector"])

```

and the output looks like this:

 ![grouped_data](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/a/a79854834d98de45c753254e8b9d1fb5d03ae814.png)

If I am not mistaken about these terms (sorry, still learning), the map ‘key’ becomes the value for each of these elements (e.g. for ‘status’, one key is ‘active’), and then the value assigned to that key becomes the corresponding ‘map’, which is an array of objects.

Answering my question on [Rendering Mapped / Grouped Data](https://talk.observablehq.com/t/rendering-mapped-grouped-data/3476), @mootari showed me how to create a new array comprised of the key name and the size of the corresponding map:

```auto
grouped_data_array = Array.from(grouped_data, ([key, map]) => ({
  name: key.toLowerCase(),
  value: map.size,
}))

```

This works great for a normal pie chart, but what if I want to ‘drill down’ deeper into each level?

To plug this into Mike’s Zoomable Sunburst chart, I’d need to perform this re-working of keys and values recursively, so that it matches up with the way the chart is expecting data (or, i think i need to do this?):

 ![flare_data_view](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/f/ff8e1f44cdacef18702f54ea461d0cd1ae360c66.png)

From what I can gather, part of this is accomplished with `d3.hierarchy`, but beyond that I haven’t figured out how I to accomplish a proper transformation (although I’ve been reading and tinkering for a few weeks now).

**Would you be willing to help me figure this out?**

**Alternatively, would you be willing to tell me this approach is folly?**

For some additional context:

I’m really guessing that it’d be possible, or relatively easy, to re-work the data rather than to tinker with the visualization code based on the idea that a visualization can be ‘reusable’. In this case, I was hoping to do something like this:

```auto
import {chart}
with {grouped_data as data}
from "@d3/zoomable-sunburst"

```

But is this really the way to go? Or should I instead be editing the visualization code itself to match the grouped data (rather than trying to make the grouped data match the expected inputs for the visualization)?

**I would really appreciate your help and insights!** 🙏

Here’s a notebook with the data I’ve been playing with:

> **[Connecting Grouped Data to Sunburst Chart](https://observablehq.com/d/6a32e7066a7be6c0)**
>
> Data Source: ADB Sovereign Projects, as of 18 January 2019

Thanks in advance, community! You’re all swell folks, and I appreciate learning from you!

#### Related Readings:

- [d3.group, d3.rollup](https://observablehq.com/@d3/d3-group)
- [d3.groups as a hierarchy](https://observablehq.com/@d3/d3-group-d3-hierarchy) // toying with this one, I’m beginning to expect it’s the tool I ought to be using…

---

<div class="post-metadata">

**Author:** ![bayre](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/bayre/32/3681_2.png) [@bayre](https://talk.observablehq.com/u/bayre)\
**Post date:** [September 25, 2020, 8:40am UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931/2 "2020-09-25T08:40:37Z")

</div>

Hey Aaron, you might find this useful [https://observablehq.com/@bayre/unrolling-a-d3-rollup#nest](https://observablehq.com/@bayre/unrolling-a-d3-rollup#nest). It’s a function that converts a d3.rollup into a nested object, the data structure that is used in most d3.hierarchy examples.

With D3 v6 you can create a hierarchy with d3.group/d3.rollup directly, but it requires the chart to be restructured a little. This diff shows the edits I made to the Treemap to get it running on d3.rollup [https://observablehq.com/compare/a1fd3857bac219b0@164...1b6b102e10f3f36d@190](https://observablehq.com/compare/a1fd3857bac219b0@164...1b6b102e10f3f36d@190).

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [September 25, 2020, 11:39am UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931/3 "2020-09-25T11:39:54Z")

</div>

Hi @bayre, and thank you!

So from the look of the resulting data, your nest function does the trick:

 ![nesting](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/d/d61f5c3c5fa81c3258ffd1109452b97d5de5dc18.png)

~~… And yet when I try plugging this into the chart, it’s still [coming up blank](https://observablehq.com/d/6a32e7066a7be6c0).~~

~~Any insights into what I’m missing?~~

**EDIT** : Sorry, I caught it - I just had to specify that I wanted the length of the final ‘value’ tier.

What a neat function! Thank you! 😄

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [September 25, 2020, 2:36pm UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931/4 "2020-09-25T14:36:08Z")

</div>

You can compute a d3.hierarchy directly from d3.rollup:

```auto
rollup = d3.rollup(sovereign_projects.slice(1), d => d.length, d => d["Status"], d => d["Country"], d => d["Sector"])

```

```auto
root = d3.hierarchy(rollup)

```

The problem is that D3 Zoomable Sunburst example uses `d => d.value` as the value accessor for computing the value of each node. If you use d3.rollup as input, then the leaf nodes are `[key, value]` entries, and so you need to say instead:

```auto
root = d3.hierarchy(rollup).sum(([, value]) => value)

```

There are a few other changes that need to be adapted from that example. Here’s a working fork with your data:

> **[Zoomable Sunburst from d3.rollup](https://observablehq.com/d/08d45f2703b4cb7f)**
>
> An Observable notebook by Mike Bostock.

And here’s what I changed:

> **[Comparing ‘Zoomable Sunburst’ by D3 to ‘Zoomable Sunburst from d3.rollup’ by...](https://observablehq.com/compare/86ddbc29bd33f9d6@353...08d45f2703b4cb7f@381)**
>
> A comparison of two Observable notebooks.

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [September 25, 2020, 10:48pm UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931/5 "2020-09-25T22:48:51Z")

</div>

Thank you, @mbostock . This is helpful!

Just to make sure that I understand all of what’s going on:

I was using `D3.group` to help me drill down into my dataset according a preferred pathway (in my case, ‘status’, ‘country’, ‘sector’). `d3.gourp` is particularly helpful in the sense that it the data are re-arranged, but otherwise intact (that is, allowing me to read through the groupings with all data attributes and values form the original data set being visible and accessible–even beyond the final grouping tier.

The solutions here, however, both rely on `d3.rollup`. Reading @Fil’s documentation:

> [d3.rollup](https://github.com/d3/d3-array/blob/master/README.md#rollup) lets you “reduce” each group by computing a corresponding summary value, such as a sum or count. The result is a map from key to reduced value.

From this, my expectation is that switching from `d3.group` to `d3.rollup` would return the data in a similar structure, but with values from the groupings rather than full, re-grouped arrays. I also expect/imagine that this is necessary as I move from wishing to ‘explore’ the data to wishing to ‘render/visualize’ the data.

But as I try to break this down to understand it, matters become very confusing, i.e. -

When I do a simple substitution, I am returned a very different looking map:

```auto
grouped_data = d3.group(sovereign_projects.slice(1), d => d["Status"], d => d["Country"], d => d["Sector"])

```

 ![grouped-data](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/9/9680e6bdc45334823dd2f7de03b3710e1f38dfb3.png)

vs.

```auto
rolled_data = d3.rollup(sovereign_projects.slice(1), d => d["Status"], d => d["Country"], d => d["Sector"])

```

 ![rolled-data](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/4/4ce83c2a1790d49076245aec7fbac3d80fd39319.png)

In your response (and now I see, too, in Fil’s example), you added a first mapping: `d => d.length`, which returns a similar structure as with `d3.group`:

 ![rollup](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/2/2deeed3dbac6553410e13f07017342237bce7aa4.png)

Drilling into the data, I see that adding the `length` mapping gives me actual number values at the final tier of my grouping rather than returning `undefined`, and I conceptually understand why this would be the case (otherwise I haven’t instructed what to do with at the end of the mapping), but I don’t quite follow why the results without specifying `length` would result is such a different in the map structure itself: in my case, why it’s returning ‘country’ groupings and totally skipping ‘status’ groupings until `length` is specified.

I suspect this relates to another statement in Fil’s notebook:

> The _reducer_ is only applied to the leaf groups: above, counting the athletes for a particular nation and sport. The reducer is not applied to the parent groups, or to the values as a whole.

**Does this imply that the `length` step is necessary to establish a ‘parent’ group, so that reduction is performed on my desired first step ‘status’?**

**And if I am working through `d3.group` to explore my data, must I still switch over to `d3.rollup` to visualize it, or can I stay with `d3.group` without causing myself a lot of headaches?**

I really appreciate everyone’s time and help!! Thank you!

---

<div class="post-metadata">

**Author:** ![yurivish](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/yurivish/32/198_2.png) [@yurivish](https://talk.observablehq.com/u/yurivish)\
**Post date:** [September 26, 2020, 12:34am UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931/6 "2020-09-26T00:34:44Z")

</div>

The second argument to `d3.rollup` is unlike the others — it is a function that takes a group and returns a value for that group. You can recover the functionality of `d3.group` from `d3.rollup` by passing in a function that just returns the group itself as the “rolled-up” value:

```auto
d3.group(xs, f1, f2, f3)

```

will compute the same result as

```auto
d3.rollup(xs, g => g, f1, f2, f3)

```

Passing in a different function, such as `g => g.length`, will use that to compute the final value. In your case, the `d => d["Status"]` function was being passed the group as the `d` argument, which is why the result was `undefined`.

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [September 26, 2020, 12:55am UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931/7 "2020-09-26T00:55:36Z")

</div>

Thanks @yurivish!

Out of curiosity, how did you know this? Looking at the [d3.rollup documentation](https://github.com/d3/d3-array#rollup), I see the pattern you explained here, but not the explanation 😉

---

<div class="post-metadata">

**Author:** ![yurivish](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/yurivish/32/198_2.png) [@yurivish](https://talk.observablehq.com/u/yurivish)\
**Post date:** [September 26, 2020, 1:09am UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931/8 "2020-09-26T01:09:52Z")

</div>

The second argument is in the documented signature as `reduce` and I’m familiar with the idea of a reduction from functional programming (e.g. [MapReduce](https://en.wikipedia.org/wiki/MapReduce)).

The docs could probably be improved to mention that for each group, the reduction function is passed an array of the values in that group and the return value is used as the value for that group.

---

<div class="post-metadata">

**Author:** ![Fil](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/fil/32/207_2.png) [@Fil](https://talk.observablehq.com/u/Fil)\
**Post date:** [September 26, 2020, 1:06pm UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931/9 "2020-09-26T13:06:30Z")

</div>

I’ve added a paragraph at [https://observablehq.com/@d3/d3-group#rollups](https://observablehq.com/@d3/d3-group#rollups) to try and clarify this point. Let me know if it can be improved.

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [September 26, 2020, 2:54pm UTC](https://talk.observablehq.com/t/how-to-move-from-d3-group-to-a-format-that-works-for-a-sunburst-chart/3931/10 "2020-09-26T14:54:52Z")

</div>

Thanks @Fil!
