JS / d3.js: creating groups for source / target links

I'm trying to color some of the nodes based on their links, but I'm not sure how. Here's my current graph:

My current schedule

I am trying to auto-color all nodes that are within the same cluster (i.e. connected to each other either as source or target for any node within the cluster), but so far all my attempts have been futile ..

I heard that I might need a recursive function, but my recursion is self taught and fundamentally wrong. Here is my current code:

function assignGroup() {
var groupedNodes = [];

for(var i = 0; i < gNodes.length; i++) {
    if(nodes[i].group !== undefined) {
        nodes[i].group = undefined;
    }
}

for(var i = 0; i < nodes.length; i++) {
    if(nodes[i].group === undefined) {
        nodes[i].group = i;
        recursive(nodes[i],i);
    }
}


function recursive(rNode, rGrp) {       
    var tempSrc = hasSrc(rNode);        
    var tempTarg = hasTarg(rNode);

    if(tempSrc == null && tempTarg == null)
        return;

    if(tempSrc != null && tempSrc.group === undefined) {
        tempSrc.group = rGrp;
        recursive(tempSrc, rGrp);
    }

    if(tempTarg != null && tempTarg.group === undefined) {
        tempTarg.group = rGrp;
        recursive(tempTarg, rGrp);
    }
}

function hasTarg(eNode) {
    for(var i = 0; i < edges.length; i++) {
        if(nodes.name == edges[i].source)
            return getNode(nodes, edges[i].target);
        else
            return null;
    }
}

function hasSrc(eNode) {
    for(var i = 0; i < edges.length; i++) {
        if (nodes.name == edges[i].target)
            return getNode(edges[i].source);
        else
            return null;
    }
}

function getNode(id) {
    console.log(id);
    for(var i = 0; i < nodes.length; i++) {
        if(id == nodes[i].name) {
            return nodes[i];
        }
    }
    return null;
}

      

My approach is to assign a group to each node and color them based on their group, essentially solving two birds with one stone (the group can be used for future implementations as well).

My dataset:

{
"nodes":
[
 {
   "name": "Ben",
},
 {
   "name": "May",
},
 {
   "name": "Jack",
},
 {
   "name": "Francis",
},
 {
   "name": "Owen",
},
 {
   "name": "Blake",
},
 {
   "name": "Julia",
},
 {
   "name": "Liam",
}
],
"edges":
[
    {
        "source":"Ben",
        "target":"May"
    },
    {
        "source":"Ben",
        "target":"Blake"
    },
    {
        "source":"Ben",
        "target":"Owen"
    },
    {
        "source":"Owen",
        "target":"Julia"
    }
]
}

      

Here is the expected output of my dataset for node:

"nodes":
[
 {
   "name": "Ben",
   "group": 1
},
 {
   "name": "May",
   "group": 1
},
 {
   "name": "Jack",
   "group": 2 /*Arbitrary group (Ungrouped)*/
},
 {
   "name": "Francis",
   "group": 3 /*Arbitrary group (Ungrouped)*/
},
 {
   "name": "Owen",
   "group": 1
},
 {
   "name": "Blake",
   "group": 1
},
 {
   "name": "Julia",
   "group": 1
},
 {
   "name": "Liam",
   "group": 4 /*Arbitrary group (Ungrouped)*/
}
]

      

Edit: Here is a jsfiddle: https://jsfiddle.net/ehnf76xg/2/

+3


source to share


1 answer


This is my decision. First, let's give each node a different number:

data.nodes.forEach(function(d, i) {
    d.group = i
});

      

The numbers themselves don't matter as you know.

Then we check for each node if its name is in source

or of target

each object in the array edges

. If so, we give its number to both source and target correspondents in an array nodes

:

data.nodes.forEach(function(d) {
    data.edges.forEach(function(e) {
        if (e.source === d.name || e.target === d.name) {
            data.nodes.find(function(f) {
                return f.name === e.source
            }).group = d.group;
            data.nodes.find(function(f) {
                return f.name === e.target
            }).group = d.group;
        }
    })
})

      

It is like an infectious process: the first node whose name is either on source

or target

is spread by its number. Again, the number itself doesn't matter.



Here's a demo:

var data = {
  "nodes": [{
    "name": "Ben",
  }, {
    "name": "May",
  }, {
    "name": "Jack",
  }, {
    "name": "Francis",
  }, {
    "name": "Owen",
  }, {
    "name": "Blake",
  }, {
    "name": "Julia",
  }, {
    "name": "Liam",
  }],
  "edges": [{
    "source": "Ben",
    "target": "May"
  }, {
    "source": "Ben",
    "target": "Blake"
  }, {
    "source": "Ben",
    "target": "Owen"
  }, {
    "source": "Owen",
    "target": "Julia"
  }]
};

data.nodes.forEach(function(d, i) {
  d.group = i
});

data.nodes.forEach(function(d) {
  data.edges.forEach(function(e) {
    if (e.source === d.name || e.target === d.name) {
      data.nodes.find(function(f) {
        return f.name === e.source
      }).group = d.group;
      data.nodes.find(function(f) {
        return f.name === e.target
      }).group = d.group;
    }
  })
})

console.log(data)
      

Run codeHide result


Now let's look at a diagram with real strength:

var data = {
  "nodes": [{
    "name": "Ben",
  },{
    "name": "May",
  }, {
    "name": "Jack",
  }, {
    "name": "Liam",
  },{
    "name": "Francis",
  }, {
    "name": "Owen",
  }, {
    "name": "Blake",
  }, {
    "name": "Julia",
  }],
  "edges": [{
    "source": "Ben",
    "target": "May"
  }, {
    "source": "Ben",
    "target": "Blake"
  }, {
    "source": "Ben",
    "target": "Owen"
  }, {
    "source": "Owen",
    "target": "Julia"
  }]
};

data.nodes.forEach(function(d, i) {
  d.group = i
});

data.nodes.forEach(function(d) {
  data.edges.forEach(function(e) {
    if (e.source === d.name || e.target === d.name) {
      data.nodes.find(function(f) {
        return f.name === e.source
      }).group = d.group;
      data.nodes.find(function(f) {
        return f.name === e.target
      }).group = d.group;
    }
  })
})

var svg = d3.select("svg")

var force = d3.forceSimulation()
  .force("link", d3.forceLink()
    .id(function(d) {
      return d.name
    }))
  .force("charge", d3.forceManyBody().strength(-2))
  .force("collide", d3.forceCollide(15))
  .force("center", d3.forceCenter(150, 70));

var edges = svg.selectAll("line")
  .data(data.edges)
  .enter()
  .append("line")
  .style("stroke", "#aaa")
  .style("stroke-width", 2);

var color = d3.scaleOrdinal(d3.schemeCategory10);

var nodes = svg.selectAll("circle")
  .data(data.nodes)
  .enter()
  .append("circle")
  .attr("r", 10)
  .style("stroke", "#444")
  .style("stroke-width", 2)
  .style("fill", function(d) {
    return color(d.group);
  })


force.nodes(data.nodes);
force.force("link")
  .links(data.edges);

force.on("tick", function() {
  edges.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;
    })

  nodes.attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    });

});
      

<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
      

Run codeHide result


+3


source







All Articles