I'm currently working on a variation of a clustered force layout uses D3’s circle-packing layout to initialize node positions.
You can find the original code at : ClusterForce Layout IV
However I can't find a way to choose the number of nodes per cluster.
For example: For a number of nodes equal to 20 (n=20), I'd like to have my 10 clusters (m=10) shared like 2 nodes each.
I tried a lot of changes using the "d3.nest()" part, but nothing worked like I wanted to.
If someone could help , thakns a lot !
Related
I have a database where I maintain an entity Ids in one table while maintaining a source and target entity on other table.
I have generated a json of nodes and links. and used the following example as a reference (thought it would be enough to only generate the json file):
Force directed graph for D3.js v4 with labelled edges and arrows
Same code with my generated data
The issue is that the graph is messy and I can't infer anything by looking at it. so what I'm trying to do is, since I am familiar with the relations between the entities I would like the nodes to be ordered top down by labels by some certain order. e.g siteref -> ro -> ba -> gr -> ca
Is that achievable at all? I have looked around for some other examples but did not find anything that could suit my needs.
You can add forceY, and optionally pass it a strength. In the forceY function you can set to return 0 for siteref, 100 for ro, and so on. Then you can tune in at your needs by playing a little bit with the strength in the links and in the different forces. Here is an updated fiddle so you can take a look at the idea. Update fiddle
I am building a web app where the user can create a sort of bipartite graph like this one:
Sometimes the user would like to build a huge graph, for example a graph in which the topmost layer has 784 nodes, as in the next picture. My application can handle the computation, but the result is ugly and meaningless:
Do you have any idea for rendering a huge layer without just drawing all the nodes but, instead, summarising them with another, prettier representation?
Until now I have thought about putting all the nodes of a "huge" layer in an empty compound node, but of course then it is not possible to draw some edges (so the huge layer would seem as it were disconnected from the graph). Another solution would be to have all layers with more than 100 nodes have exactly 100 nodes, and put them inside a compound node with z-index greater than each node's z-index; but I haven't tried this yet.
If you have some other ideas, or if Cytoscape.js provides a way to summarise large graphs, please let me know.
Thank you.
you could group nodes when the amount in a layer exceeds a certain number (e.g. if you have 100 nodes, you combine them into groups of 25)
I'd do this by iterating over the nodes in the layer, making a new node N for each subgroup (inserting all relevant information needed), and then replacing any mention of the replaced nodes by N in all relevant edges (finding connected edges).
As I personally generate layouts/nodes in python before sending them to cytoscape for visualization, I'll refrain from posting a potentially ineffecient/incorrect javascript/cytoscape example :)
I've been working on a network topology visualization using the force-directed algorithm built into D3. Everything is working well but having troubles with one important detail... I can't seem to get the graph to layout in an ideal way for graphs with a varying number of nodes. By ideal, i mean the nodes are nicely spaced out from each (no overlap) and nodes cluster wherever it makes sense. I've been trying to do this by adjusting the 'charge' and 'gravity' properties of the force layout, but no matter what i try, it seems to always work for one scenario (ie. large number of nodes), but not for another scenario (ie. small number of nodes). For example, if i have the layout working for a large graph, then when i look at small graph using the same formula for charge/gravity, ill have a few nodes that are way way out of site from the rest of the nodes. Here's an example of a formula i was using based on another SO question post:
var k = Math.sqrt(json.nodes.length / (dim.w * dim.h));
var charge = -10 / k;
var gravity = 100 * k;
This works for a graph with 14 nodes, but if i try the same with a graph of 5 nodes, some of those nodes are completely off the screen. Note that the width/height used in the calculation of 'k' is not changing between these two scenarios. Now maybe i shouldn't have these properties based on the width/height of visible area of graph. To be honest, this is not a requirement. I don't need the graph to render and fit within the viewport of graph. I just need the graph to lay itself out sensibly, so it's fine if some of it may be outside of visible area, especially in a large graph. I've also tried the following with some success, but i still find nodes are being rendered too far away from the rest of the graph for small graphs:
var charge = -1 * Math.pow(json.nodes.length, 3);
var gravity = 1 / json.nodes.length;
Can anyone out there help me out with this ? Would be greatly appreciated as i feel kind of stuck on this atm.
I actually figured this out on my own...
So the values i was using for charge/gravity/etc were not so much the problem. The issue was related to how many times the tick function was being called to adjust the graph. For my larger graphs, the nodes were always laid out fairly well. The main problem i was having was with smaller graphs. I was finding that nodes would frequently be placed outside of the viewport when the graph only had around 5-10 nodes in it.
In my code, im calling the tick function manually, like so:
force.start();
for (var i = tickLimit; i > 0; --i)
force.tick();
force.stop();
Previously, tickLimit was being set as follows:
var tickLimit = Math.pow(json.nodes.length, 2);
After messing around with charge/gravity values etc, I eventually realized that this is not sufficient for small graphs. If i have a graph with 4 nodes, then that means only 16 tick() calls will be made. This is not enough for the graph to adjust itself fully (ie. stabilize). Therefore i just needed to add a check to ensure the graph ticks a minimum number of times (ex. at least 300), and a maximum (ex. no more than 10000).
This may not work for everyone but it solves the issue for me.
In this force based algorithm case I would say that it's almost impossible to set all-cases-fit settings. This layout'd hardly depend on graph density and inner graph semantic.
What is the range of possible number of nodes? What about density? Is it randomly generated graph with predefined density coefficient or it has some semantic behind and has possibility to look good based on this semantic.
You said that nodes flows far away from each other. What higher gravity gives you?
Also the suggestion about linkDistance may help you too. E.g. I'm using d3.forceLayout also for drawing network graphs (but they're mostly small handmade graphs with nodes < 50). And I just copied stats from one of Mike Bostock's force layout example. Here they're:
// graph force layout defaults
var linkDistance = 50,
charge = -200;
// chart properties
var height = 720,
width = 720;
radius = 10;
I don't expect that it'll help you but maybe it'll stimulate the others to discuss.
UPD. All I can suggest you is to experiment. Choose a small set of test graphs and find optimal init settings for every one, then interpolate given numbers. Also if you deal with very big graphs (very big for "nice" visualization I mean) maybe you'll find grouping (colapsing) some parts of it helpfull - it reduces the number of nodes (and maybe the graph complexity).
Also keep in mind you don't need to set constant force layout settings (charge, gravity, linkDistance and etc. are all maintain functions). And you can set radius of nodes a little bit more than visible radius so they won't overlapping each other. Or set non constant function for charge, e.g. smth like this. Or use Mike Bostock advice to spread nodes on each tick manually.
I'm trying to progressively combine two groups of nodes to simulate a sort of adhoc pie chart using a force layout. To do this, I'm using two groups of nodes connected via links to two fixed, animated, foci nodes, respectively. When they combine with ruffly the same number of nodes, the effect works well.
see: http://bl.ocks.org/vicapow/6191895
however, when there's a large difference in the groups of nodes, (say, 1 / 100 ), there seems to be some mysterious extra force applied to the smaller group of nodes away from the larger group
see: http://bl.ocks.org/vicapow/6191896
Does anyone have any ideas on where this extra force could be coming from?
some of the things I've tried that didn't work:
+ forcing the alpha to stay 1
+ reducing the friction
I believe what you are seeing is the lack of gravity. Without gravity the nodes will not tend towards the center of the screen. I was able to get the desired result for your edge case by setting force.gravity(1) and the charge for the nodes to -100. https://bl.ocks.org/david4096/6264697
Not sure if I'm using the right terminology so this question may already be answered somewhere on stackoverflow (if so, please let me know). Is there a way to designate multiple "gravity points" in a d3.js graph where the gravity point is one node with a fixed (x,y) value in each cluster? For example, I'm using the code from this example to display clusters. The only difference in what my graphs will look like is that the nodes will have links to each other (possibly to nodes in other clusters as well). I want to make sure the other clusters overlap as little as possible and still show any links between each cluster.
I've tried force.gravity(0.1) with force.charge(-10). That seems to keep the clusters apart but the between-cluster distance continues to decrease as gravity increase because there is only one "gravity point" located in the middle of the graph by default.
Only way i can think of is to add a node where you want to keep gravity and designate its charge with a large positive value , rest of the nodes should have a negative value charge. Hopefully they will attract towards each other. If you have found a better solution , please let me know.