Formatting nodes in networkx to be more readable - javascript

I have a spring layout for NetworkX (rendered in cytoscape.js) which seems to be working well for small datasets, but when it gets really large, nodes keep getting too close together and makes it unreadable. The further out you go in the graph, nodes end up spreading out evenly enough. But the closer in you go to the enter (blue squares) the more compact everything is even though there's still plenty of space.
Here's the algorithm I'm trying to use:
pos = nx.spring_layout(G, k=5/np.sqrt(G.order()), iterations=150, scale=8000)
print('after',pos)
nx.draw(G, pos,node_size=2)
I tried making k=6, but it just increases the distance from it's parent node, not any nodes that might be in the vicinity.
I'm hoping to find a way to make it more readable and have the nodes be more spread out.

Related

Rearrange Points that are too near to each other

My case is that I am dynamically positioning a bunch of icons on a static map image, each by CSS, positioned absolutely. Now it often happens, that two or even more points are too near to each other, so the icons overlap and they are not anymore distinguishable.
I am looking for an algorithm to find these "too near to each other" points, and then spread their icons out in a manner that they do not overlap each other anymore.
I am thinking of a radial spread, like finding the average middle point of all points that are too near and then spread them out relatively to that point.
Is there any pattern for such a problem of which you may know?
Thanks a lot in advance.
Here are a few solutions that might solve your problem:
Use a solution to the closest pair of points problem to find the two icons that are closest to one another. If the closest pair is "too close" by your definition, you can move them apart from one another and repeat this process.
Use a spatial data structure like a k-d tree or R-tree to store all the points. You can then execute fast nearest-neighbor searches to find points that are close to one another and move them apart.
Use a force-directed layout algorithm to find a layout for the points that globally minimizes some energy function. Algorithms like Fruchterman-Reingold are pretty straightforward to code up and produce good results.
Hope this helps!

d3.js tree layout need to expand as nodes open, not compress

I'm using the tree layout and code similar to http://mbostock.github.io/d3/talk/20111018/tree.html
I modified it for a top down orientation.
As each node is opened/expanded, the other open nodes compress to fit everything within the SVG element. Is it possible to prevent that? I would think modifying the x component of each node would be the approach but have not been able to accomplish that. The nodes move over, but are still compressed together.
Also wondering how to change the linking lines from a bezier to right angles/straight lines. Perhaps a separate question is needed.
The compression is automatic in the tree layout (and part of its point). There's no way to turn that off. However, you can simply make your SVG large enough to contain the entire expanded tree without compression. Note that this means that unless your screen is large enough scroll bars will be displayed even when everything that is visible fits onto the screen.
The links connecting the nodes are generated using the diagonal line generator in the example. In principle, you can replace this with any other line generator (e.g. d3.svg.line), but in practice some changes will be necessary because the diagonal line generator accesses source and target nodes in a special way. For a normal line generator, you would need to convert this structure to a two-element array and for each element specify how to access x/y coordinates. Then you can use any of the interpolations to get the curve you want.

Optimize d3 force directed layout, via charge/gravity properties, based on number of nodes

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.

Mysterious forces in d3 force directed layout?

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

Efficiently calculating convex hulls in D3 force-directed graph

I've got something similar to the force-directed graph example. The main difference is there is no force -- the layout is static except for user drag interactions. I've added code that draws convex hulls (as svg:path elements) around user-defined groups of nodes. As the nodes are moved (that is, .on("drag")) the code that calculates the hulls is fired. That means it's fired constantly when the nodes are dragged. There are typically 10 to 30 hulls; a node may be in one or more hull. See below:
I'm trying to figure out if there's a more efficient (higher performance) way to do what I'm doing. Keeping this at high-level for now:
On drag, update all hull graphics:
For each hull, create an array of the coordinates of the constituent nodes.
Replace each coordinate pair in the above array with 8 points that are some distance r away from the original point. This is to pad/dilate the hull so it's not actually touching any nodes.
Feed the calculated coordinates to d3.geom.hull.
I'm getting about 50 fps in Chrome, which isn't bad at all, but it seems like a dreadfully inefficient setup.
My only clear idea is to store the ID(s) of the hull(s) in which a node is contained in the nodes array, and only update that/those hulls instead of all of the hulls. I'm also wondering about more efficient data structures to store the hull data (not the paths themselves). Currently the data structure looks like this:
hulls = {1:{nodeIDs:[1,2,3,4,5], name:"hull1"}, 2:{...}, ...};
Pardon the open-ended question, but I'm hoping there's some programming technique that I'm not familiar with that would work well here.
The most efficient approach would be to only recalculate the position of the 8-tuple of coordinates orbiting the node that is being dragged, then propagating that information in the parent hull(s) for redrawing.
You can do this by making a few changes.
To each node's data structure, add the following elements.
References to all parent hulls (as you suggested)
The 8-tuple of "padding" coordinates orbiting that node (cache them)
On drag, consider only the node being dragged.
Update that node's 8-tuple.
Collect that node's parent hull's child nodes and collapse together their 8-tuples and feed them to hull (destroying the previous hull, if necessary).
I'm not familar with d3.js's API, but I'm sure you can fill in any blanks that I've left.

Categories

Resources