Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flatten an Object hierarchy created with d3.js nesting

I am trying to visualize team collaboration data, in a way like this:

Chart that displays the share of the different collaboration artifact types for each team and week

Different colors in the chart are different collaboration artifact types.

The data from the source looks like this:

var json = [
    {
        "teamLabel": "Team 1",
        "created_date": "2013-01-09",
        "typeLabel": "Email"
        "count": "5"
    },
    {
        "teamLabel": "Team 1",
        "created_date": "2013-01-10",
        "typeLabel": "Email"
        "count": "7"
    },
    /* and of course, a lot more data of this kind */
]

Note that the data is given for single days. So for the above visualization, I need to aggregate the data based on the week of year first. The team name and the artifact type need to be preserved though and are used as grouping attributes. Here's the code:

// "2013-01-09"
var dateFormat = d3.time.format.utc("%Y-%m-%d");

// "2013-02" for the 2nd week of the year
var yearWeek = d3.time.format.utc("%Y-%W");

var data = d3.nest().key(function(d) {
        return d.teamLabel;
    }).key(function(d) {
        var created_date = dateFormat.parse(d.created_date);
        return yearWeek(created_date);
    })
    .key(function(d) {
        return d.typeLabel;
    }).rollup(function(leaves) {
        return d3.sum(leaves, function(d) {
                return parseInt(d.count);    // parse the integer
            });
        }
    )
    .map(json);

This results in an Object hierarchy based on the nesting keys. I do not see how to create the above chart from this, so I am rather looking for a way to convert data into the following structure:

[
    // This list contains an element for each donut
    {
        "teamLabel": "Team 1",
        "createdWeek": "2013-02",
        "values": [
            // This list contains one element for each type we found
            {
                "typeLabel": "Email",
                "count": 12
            },
            {
            ...
            }
        ]
    },
    {
    ...
    }
]

This way, I can use createdWeek and teamLabel for the positioning on x- and y-Axis respectively, and the information under values can be passed to d3.layout.pie().

Is there a clean way to do this data transformation? If you need any clarification or further details, please let me know.

like image 513
cyroxx Avatar asked Jan 11 '13 00:01

cyroxx


1 Answers

That's how you do it:

var flat = data.entries().map(function(d){
    return d.value.entries().map(function(e){
        return {
            "teamLabel": d.key,
            "createdWeek": e.key,
            "values": e.value.entries().map(function(f){
                return {"typeLabel": f.key, "count": f.value}
            })
        }
    })
}).reduce(function(d1,d2){ return d1.concat(d2) },[]);

Note that I'm using d3.map instead of the standard javascript object in order to use the map.entries() helper function. I imagine that's what you tried judging by the fact that you're using:

.map(json); // whereas I used .map(json, d3.map)

instead of

.entries(json);

jsFiddle link here:

http://jsfiddle.net/RFontana/KhX2n/

like image 180
Renaud Avatar answered Sep 30 '22 21:09

Renaud