Matt-Heun Hong
Matt-Heun Hong

Reputation: 406

CSV to D3: summing over values for multiple links

I'm drawing a network graph on D3 with data that looks like this:

target source value

Baird   JON CARLSON 100    
Baird   JASON CONRAD    100    
Baird   JASON CONRAD    100    
Baird   JASON CONRAD    100    
Baird   JANET ESKRIDGE  100    
Baird   LINDA GARNER MULLIN 100    
Baird   JAMES HARRIS    100

...etc.

The three "Baird | JASON CONRAD | 100" rows are not duplicates; I want D3 to sum over the individuals to give "Baird | JASON CONRAD | 300" before it inputs the link.

I'm new to JS and D3 and don't really have a clue how I'd go about that, but here's your classic csv import function in question:

d3.csv("xxx.csv", function(error, links) {

var nodes = {};

links.forEach(function(link) {
    link.source = nodes[link.source] || 
      (nodes[link.source] = {name: link.source});
    link.target = nodes[link.target] ||
      (nodes[link.target] = {name: link.target});
    link.value = +link.value;
});

force

  .nodes(d3.values(nodes))
  .links(links)
  .start()

EDIT

I used nest() to sum the values up, but now I've no idea how to input the result into usable links and nodes arrays for force().

Any ideas? This is my nest() code:

var nested_links = d3.nest()  
    .key(function(link) { return link.source; })  
    .key(function(link) { return link.target; })  
    .rollup(function(link) { 
        return d3.sum(link, function(l) { return l.value; });  
    }).entries(links);

Upvotes: 0

Views: 1388

Answers (1)

jkutianski
jkutianski

Reputation: 570

As @meetamit said you can use d3.nest() for this task.

How d3.nest() works?

if you have

var data = [
    ["Baird JON CARLSON", 100],
    ["Baird JASON CONRAD", 100],
    ["Baird JASON CONRAD", 100],
    ["Baird JASON CONRAD", 100],
    ["Baird JANET ESKRIDGE", 100],
    ["Baird LINDA GARNER MULLIN", 100],
    ["Baird JAMES HARRIS", 100]
];

and execute

var nested_data = d3.nest()
                    .key(function(d) {
                        return d[0];
                    })
                    .entries(data);

nested_data will be something like this

[{
    "key":"Baird JON CARLSON",
    "values":[
        ["Baird JON CARLSON",100]
    ]
 },{
     "key":"Baird JASON CONRAD",
     "values":[
        ["Baird JASON CONRAD",100],
        ["Baird JASON CONRAD",100],
        ["Baird JASON CONRAD",100]
     ]
 },{...
}]

with Javascript normaly you can use Array.reduce() for reduce the array to the sum of the values, but with D3js the best option is use d3.nest().rollup(). D3jshas too a helper to reduce an Array() to a sum of the returned value and if you have

var values = [
    ["Baird JASON CONRAD",100],
    ["Baird JASON CONRAD",100],
    ["Baird JASON CONRAD",100]
];

then you can make

 var sum = d3.sum(values, function(d) {
               return parseFloat(d[1]);
           });

and sum will be 300. Glueing all this will be

var nested_data = d3.nest()
                    .key(function(d) {
                        return d[0];
                    })
                    .rollup(function(leaves) {
                        return d3.sum(leaves, function(d) {
                            return parseFloat(d[1]);
                        });
                    })
                    .entries(data);

after that you'll have on nested_data something like this

[{
    "key":"Baird JON CARLSON",
    "values": 100
 },{
    "key":"Baird JASON CONRAD",
    "values": 300
 },{
    "key":"Baird JANET ESKRIDGE",
    "values": 100
 },{
    "key":"Baird LINDA GARNER MULLIN",
    "values": 100
 },{
   "key":"Baird JAMES HARRIS",
   "values": 100
}]

Check my Fiddle here

To create your link array as you need it, you've to map the keys and values to the correct position on the array. Take on care that if you use 2 key function you will finish with a double nested object.

var nested_data = d3.nest()
                    .key(function(d) {
                        return [d[0],d[1]];
                    })
                    .rollup(function(leaves) {
                        return d3.sum(leaves, function(d) {
                            return parseFloat(d[2]);
                        });
                    })
                    .entries(data)
                    .map( function (d) {
                        var val = d.key.split(',');
                        return {source: val[0], target: val[1], value: d.values};
                    });

Check my Fiddle here

Upvotes: 2

Related Questions