# From absolute values to percentages in a rollup?

**URL:** <https://talk.observablehq.com/t/from-absolute-values-to-percentages-in-a-rollup/6787>\
**Category:** Help\
**Created:** [July 11, 2022, 9:38am UTC](https://talk.observablehq.com/t/from-absolute-values-to-percentages-in-a-rollup/6787 "2022-07-11T09:38:49Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![marcf](https://avatars.discourse-cdn.com/v4/letter/m/c5a1d2/32.png) [@marcf](https://talk.observablehq.com/u/marcf)\
**Post date:** [July 11, 2022, 9:38am UTC](https://talk.observablehq.com/t/from-absolute-values-to-percentages-in-a-rollup/6787/1 "2022-07-11T09:38:49Z")

</div>

I’m trying to get the syntax right to return the percentages rather then the absolute values in this example:  
[https://observablehq.com/@d3/d3-group#cell-165](https://observablehq.com/@d3/d3-group#cell-165)

So the result I’m looking would look something like this:  
`"United States" => Map(3) {"Boxing" => 0.2, "Basketball" => 0.4, "Football" => 0.4}`

Basically `v => v.length` needs to be divided by the total number of this group.

Any pointers in the right direction are much appreciated.

---

<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:** [July 11, 2022, 11:33am UTC](https://talk.observablehq.com/t/from-absolute-values-to-percentages-in-a-rollup/6787/2 "2022-07-11T11:33:29Z")

</div>

you might do it in two nested calls to rollup:

```auto
d3.rollup(
  athletes,
  byNation => d3.rollup(byNation, bySport => bySport.length / byNation.length, d => d.sport),
  d => d.nation
)

```

---

<div class="post-metadata">

**Author:** ![marcf](https://avatars.discourse-cdn.com/v4/letter/m/c5a1d2/32.png) [@marcf](https://talk.observablehq.com/u/marcf)\
**Post date:** [July 11, 2022, 1:48pm UTC](https://talk.observablehq.com/t/from-absolute-values-to-percentages-in-a-rollup/6787/3 "2022-07-11T13:48:39Z")

</div>

Thank you @Fil !

I’m just curious how byNation can be used both as the return result as well the input parameter in the nested rollup.

---

<div class="post-metadata">

**Author:** ![mythmon](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mythmon/32/8504_2.png) [@mythmon](https://talk.observablehq.com/u/mythmon)\
**Post date:** [July 11, 2022, 3:13pm UTC](https://talk.observablehq.com/t/from-absolute-values-to-percentages-in-a-rollup/6787/4 "2022-07-11T15:13:47Z")

</div>

A more verbose version of what Fil wrote that is (mostly) equivalent is this:

```nohighlight
d3.rollup(
  athletes,
  function outerRollup(byNation) {
    return d3.rollup(
      byNation,
      function innerRollup(bySport) {
        return bySport.length / byNation.length;
      },
      function innerKey(datum) {
        return datum.sport;
      }
    );
  },
  function outerKey(datum) {
    return datum.nation;
  }
)

```

The function `outerRollup` introduces a new variable scope, which contains the variable `byNation`. That can be used anywhere inside it’s scope, and can be used multiple times.

The function `innerRollup` is contained inside of `outerRollup`, and so is contained in its variable scope, and can access any variable available to `outerRollup`.

Does that help understand why `byNation` can be used in both places?

---

<div class="post-metadata">

**Author:** ![marcf](https://avatars.discourse-cdn.com/v4/letter/m/c5a1d2/32.png) [@marcf](https://talk.observablehq.com/u/marcf)\
**Post date:** [July 15, 2022, 2:10pm UTC](https://talk.observablehq.com/t/from-absolute-values-to-percentages-in-a-rollup/6787/5 "2022-07-15T14:10:30Z")

</div>

Yeah, that definitely helps to understand a bit better what’s going on under the hood. As byNation is passed as a variable into outerRollup it’s also available within the innerRollup, correct?

One more question about this. How is the value of byNation specified as the variable for the outerRollup?

---

<div class="post-metadata">

**Author:** ![mythmon](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mythmon/32/8504_2.png) [@mythmon](https://talk.observablehq.com/u/mythmon)\
**Post date:** [July 15, 2022, 3:39pm UTC](https://talk.observablehq.com/t/from-absolute-values-to-percentages-in-a-rollup/6787/6 "2022-07-15T15:39:19Z")

</div>

> As byNation is passed as a variable into outerRollup it’s also available within the innerRollup, correct?

Exactly, yes.

> How is the value of byNation specified as the variable for the outerRollup?

That’s just how `d3.rollup` is defined. To get a bit more insight into that, lets consider how `d3.rollup` is implemented. [The actual source code](https://github.com/d3/d3-array/blob/04f3423768daeda9fc6109687b5c1aa31000c278/src/group.js#L27-L29) of it is a bit intense, but here is a version that for the purposes of this discussion is the close enough:

```js
function toyRollup(array, rollupFunction, keyFunction) {
  // Separate all the values of arrays into groups, based on the keyFunction
  let groups = new Map();
  for (let element of array) {
    let key = keyFunction(element);
    if (!groups.has(key)) {
      groups.set(key, []);
    }
    groups.get(key).push(element);
  }

  // For each group, use the rollupFunction to combine it into a single value
  let rolledUpData = new Map();
  for (let [key, group] of groups.entries()) {
    let combinedValue = rollupFunction(group);
    rolledUpData.set(key, combinedValue);
  }

  return rolledUpData;
}

```

If we then consider just a one layered version of the problem where we just want to calculate the number of athletes in each nation:

```js
function rollupFunction(athletesOfOneNation) {
  return athletesOfOneNation.length;
}
function keyFunction(athlete) {
  return athelete.nation;
}
let numberOfAthletesInEachNation = toyRollup(athletes, rollupFunction, keyFunction);

```

then we can substitute the value into the definition of `toyRollup`. This is what the solution to the question about the number of athletes in each nation would look like without d3.rollup:

```js
function calculateNumberOfAthletesInEachNation(athletes) {
  let athletesGroupedByNation = new Map();
  for (let athlete of athletes) {
    let key = athlete.nation;
    if (!athletesGroupedByNation.has(key)) {
      athletesGroupedByNation.set(key, []);
    }
    athletesGroupedByNation.get(key).push(athlete);
  }

  let numberOfAthletesByNation = new Map();
  for (let [nation, group] of athletesGroupedByNation.entries()) {
    numberOfAthletesByNation.set(nation, group.length);
  }

  return numberOfAthletes;
}

```

Hopefully that helps make sense of how the data moves around during the rollup process! I also hope that now you can see the appeal of having an API that can turn that complicated function into something as compact as this:

```js
const numAthletesByNation = d3.rollup(athletes, ds => ds.length, d => d.nation);

```
