How to count the number of children in a d3.nest() generated tree

3,216 views
Skip to first unread message

sspboyd

unread,
Oct 23, 2011, 5:14:02 PM10/23/11
to d3...@googlegroups.com
I have a csv doc that I've used the nest function on:
var dnest = d3.nest()
.key(function(projs) {
return projs["Project Owner"]
})
.entries(d);

The var dnest now has 8 objects for the different Project Owners. I am trying to figure out how to write a function that will take a list of one or more Project Owners (eg, TV, Sports) and return the total number of projects for those Project Owners. My attempts so far haven't worked

I feel like I'm fumbling around in the dark trying to get the right combination to pull out such a simple number. 

I've started by trying things like the code below to even just get a number for the total number of projects but that's failing too.
d3.sum(d3.values(dnest)[i].values.length)
d3.sum(d, d3.values(dnest)[i].values.length)

Any suggestions are much appreciated.
Stephen

Mike Bostock

unread,
Oct 23, 2011, 8:33:27 PM10/23/11
to d3...@googlegroups.com
> I am trying to figure out how to write a function that will take a list of one or
> more Project Owners (eg, TV, Sports) and return the total number of projects
> for those Project Owners.

Two things to look at in this case are the nest operator's `map` and `rollup`.

The `map` operator (as opposed to `entries`) returns an associative
array, mapping from key to value. This is useful in your case because
you'll want to lookup a value (the count of projects) for a particular
owner. So to do this efficiently, you'll want a map from owner name to
count of projects.

The `rollup` operator is convenient for producing a single value from
the array of matching objects for a particular key, in conjunction
with the nest operator. In this case, you'd want to reduce the array
of projects for each owner to a simple count. Putting this together,
assuming that `projects` is an array of projects:

var countByOwner = d3.nest()
.key(function(project) { return project["Project Owner"]; })
.rollup(function(projects) { return projects.length; })
.map(projects);

Now if you look at this object, it should be like this:

{
"TV": 24,
"Sports": 18,

}

So, to retrieve the count of projects owned by TV, it is
countByOwner["TV"], which equals 24. The next step is that you have an
array of project owners, and you want to sum the counts for all
associated projects. If you hard-coded it, that would look like this:

var sum = countByOwner["TV"] + countByOwner["Sports"] + …;

Let's say that you had your list of owners that you want to sum:

var owners = [
"TV",
"Sports",

];

The other way that you can add the counts is to use d3.sum. Using the
accessor function—the second argument to d3.sum—we can lookup the
count for each owner. So the first argument is the array of owners,
and the second argument is a function that maps the owner to the
associated count from the previously-computed map:

var sum = d3.sum(owners, function(owner) { return countByOwner[owner]; });

If you find yourself stumbling, one technique that can help is
sketching out an algorithm on paper first, without thinking of it in
terms of D3 operators. (Your question isn't D3-specific at all, but a
generic JavaScript and data structures question.) Once you have a
basic strategy in mind, see if the D3 operators can help you implement
it. If they can, great! Explore them interactively using the developer
console. If not, you might find it easier to just write your own code
for manipulating data.

For example, if you just wanted to get the count of projects for a
single owner, another approach is to scan the array of projects and
count the ones that match the specified owner. This can be done using
JavaScript's built-in filter method:

var count = projects.filter(function(d) { return d["Project Owner"]
== "TV"; }).length;

Mike

sspboyd

unread,
Oct 25, 2011, 10:59:10 PM10/25/11
to d3...@googlegroups.com
Thanks Mike for the response here and the answer on the other question I'd asked. They're both for the same project. 
I'm running out of runway to put this together using D3 but this is a big help to see how you'd use the library to solve the problems I'm having with my own data. 
Reply all
Reply to author
Forward
0 new messages