Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3.js rollup on nesting operator with multiple keys

I am working on a d3.js visualization for a time reporting application. I have row data in an array actuals containing project time reports (simplified):

[{  resource: "John Smith",
    reporting_period: "2012/04/1",
    project: "Java implementation",
    hours: 8} 
 ... }]

I am trying to use the d3.nest operator to group the project actuals hierarchically by project, resource and period. Everything works great but I cannot find a way to get the subtotals of hours at the intermediate levels of the grouping using the nest.rollup() operator.

I have something like:

actuals_by_prj_rsrc_period = d3.nest()
        .key(function(d) { return d["project"]; })
        .key(function(d) { return d["resource"]; })
        .key(function(d) { return d["reporting_period"]; })
        .rollup(function(rows) {return {
            tot_hours:d3.sum(rows, function(d) {return d["hours"];}),
            actuals: rows
        };})
        .entries(actuals);

but it returns tot_hours only at the leaf level. Any advice on how to approach this using only d3.nest?

like image 813
Paolo Bozzola Avatar asked Nov 01 '12 21:11

Paolo Bozzola


1 Answers

from docs:

nest.rollup(function)

Specifies a rollup function to be applied on each group of leaf elements. The return value of the rollup function will replace the array of leaf values in either the associative array returned by the map operator, or the values attribute of each entry returned by the entries operator.

As you can see rollup works with leaf elements. You could bypass this by having data nested at multiple levels:

function nest(keys, data, rollupFunc) {
    var nst = d3.nest();
    keys.forEach(function(key) {
        nst.key(function(d) { return d[key]; });
    });
    if (rollupFunc) {
        nst.rollup(rollupFunc);
    }
    return nst.entries(data);
}

var rollupFunction = function(d) {
    return {
        "total_hours": d3.sum(d, function(dd) { return dd["hours"]})
    }
}

var rez1 = nest(["project", "resource"], actuals);
var rez2 = nest(["project"], actuals, rollupFunction);
var rez3 = nest(["project", "resource"], actuals, rollupFunction);

But this very inefficient for larger data sets. Otherwise I would suggest using nest() function to create all intermediate levels. Then you could aggregate total hours using your own recursive function. Pseudocode:

function aggregate(node) {
    if (node has property total_hours) {
        return total_hours
    }
    sum = 0
    foreach child in data.values {
        sum += aggregate(child)    
    }
    node.total_hours = sum
    return node.total_hours 
}
like image 192
Žilvinas Rudžionis Avatar answered Oct 14 '22 07:10

Žilvinas Rudžionis