Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot read property 'weight' of undefined in d3.force implementation

I've been stuck on this problem for a while now and have no idea what to do even based on existing answers. I keep getting this error on the last JSON entry of a uniform response.

...{"paperCount": 1, "PMIDs": [20626970], "authorA": 79, "authorB": 80}, 
{"paperCount": 1, "PMIDs": [20492581], "authorA": 81, "authorB": 82}, 
{"paperCount": 1, "PMIDs": [20492581], "authorA": 81, "authorB": 83}, 
{"paperCount": 1, "PMIDs": [20492581], "authorA": 81, "authorB": 84}, 
{"paperCount": 1, "PMIDs": [20492581], "authorA": 82, "authorB": 83}, 
{"paperCount": 1, "PMIDs": [20492581], "authorA": 82, "authorB": 84}, 
{"paperCount": 1, "PMIDs": [20492581], "authorA": 83, "authorB": 84}]...

The code is below to extract that information. It keeps failing in the for loop...

  $.getJSON('/papers.txt', function(response){
    var w = 1280,
    h = 900,
    fill = d3.scale.category10(),
    nodes = d3.range(1000).map(Object),
    links = d3.range(1000).map(Object);

    var index = 0;
    var mouseX = 0,
      mouseY = 0;

    for (var j = 0; j < response.length; j++){
      links[j] = {source: nodes[response[j].authorA], target: nodes[response[j].authorB], value: response[j].paperCount};
    }

Any help would be appreciated.

Thanks!

like image 730
jasdeepg Avatar asked Dec 04 '22 00:12

jasdeepg


1 Answers

Introduction

Before all, I suggest that you take a long look at the documentation as all I am going to say in this answer is written in it

Well, first in the code you provide you don't use the force layout at all, you don't declare the nodes with it, you don't include the nodes in the svg... So basically, from what I see, there is no doubt that it throws an error. You should take a look at this example to see how to create a minimal force directed graph.

I would also advise that the next time you have a question, you build a jsFiddle so we can work on it and see where the problem is, because with the code you provide it is not possible to see exactly where it went wrong as you don't use d3.js in a conventional manner.

So, let's go back to the basics.

Define the graph

First, before loading the data, we need to define the general properties of the graph:

var width = 960,
    height = 500;

var color = d3.scale.category20();

/* Here we define that we will use the force layout an we add some parameters to setup the basic layout of the graph */
var force = d3.layout.force() 
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

/* Here we include the graph in the page */
var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

However, for now the graph has no nodes. We need to load the data.

Load the data

To load the data, d3 provides an helper, so let's use it instead of jQuery's one, no need to mix another library. For information, the json loader needs a .json file, so don't use .txt files even if they contain json formated data.

d3.json("papers.json", function(error, data) {
    // do whatever you need to do with the json data, for this example : create the graph
}

All that I will do later should be included inside these braces.

Format the data

From what I see here your data seems to be only a list of links, I will just consider that your nodes are just points from 0 to 99.

var my_nodes = d3.range(100).map(function(d){return {"name": d};});
var my_links = my_data.map(function(d){return {"source": d.authorA, "target": d.authorB, "value":d.paperCount, "origin": d};});

Build a force directed graph

To build the force directed graph out of my data,

force // I use the previously defined variable force to add the data.
    .nodes(my_nodes)
    .links(my_links)
    .start() //start the simulation

Now, the force layout simulation is on, it knows that there is data, but still, nothing is added to the svg. We will now add everything:

var link = svg.selectAll(".link")
    .data(my_links)
    .enter().append("line")
    .attr("class", "link")

var node = svg.selectAll(".node")
    .data(my_nodes)
    .enter().append("circle") // If you wonder why enter() is used, see the documentation
    .attr("class", "node")
    .attr("r", 5)
    .style("fill", function(d) { return "red"; })
    .call(force.drag);

For the class attributes, see example.

Declare the tick() function

Finally, you have to declare the tick function that will be called on each iteration of the force layout function

force.on("tick", function() {
  link.attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) { return d.target.x; })
      .attr("y2", function(d) { return d.target.y; });
        
  node.attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });
});

Conclusion

I just translated the basic example for your data to show it works: http://bl.ocks.org/ChrisJamesC/5028030


Or...

Maybe, the main problem you had is that you didn't understand that $.getJson() function is asynchronous so you should not just build the links vector in it, you should also use it only in this function because otherwise it won't be loaded yet.


Or...

Maybe, your error just comes from the fact that you should name the json file /papers.json instead of /papers.txt.

like image 129
Christopher Chiche Avatar answered Dec 06 '22 02:12

Christopher Chiche