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 :)
Related
I have a d3 simulation that groups nodes dependent on their properties, as calculated by an algorithm. Upon a number of iterations of this algorithm, I want to be able to display a 'cluster area' around certain groups, preferably through aggregating collided nodes while the algorithm is still running. So, rather than taking the approach of this block, where nodes are removed upon collision, is it possible to aggregate a count of collided nodes? And perhaps in a different way than noted in that block (seems to be computationally expensive with hundreds of data nodes)? I've looked through documentation for d3-force and couldn't find anything that struck out.
Thank you!
I need to use the algorithm of forces with a network, but that network is divided into parts.
For example, the vertices of the first part can not leave the part 1. The vertices of the part 2 need to be in part 2.
If there is a connection between a vertex from part 1 and a vertex from part 2, this connection will make these two vertices be near, however, will not let their parts.
This draft image illustrates the idea:
I need to do this for about 8 parts. Some parts are in the other, other parts are next to each other. And the network will be represented on these parts, each vertex in their respective part, however, the algorithm forces should try to pull the connected vertices themselves.
My solution was to create constraints using the "tick" function from d3.
To improve performance and avoid "locked" vertices on corners, I decided to use only circles.
For each node:
Make sure it is inside its region.
Make sure it is also not inside each of the other regions.
This ended up in lots of calculations for each node vs. each region. This is summed with the collision detection complexity. Yet, it handled networks with around a thousand nodes.
Details here:
Heberle, H., Carazzolle, M., Telles, G. et al. CellNetVis: a web tool for visualization of biological networks using force-directed layout constrained by cellular components. BMC Bioinformatics 18, 395 (2017). https://doi.org/10.1186/s12859-017-1787-5
Note for potential improvement: one of the most expensive calculations is the combo of checking over what elements a node is. For instance, if there was a native "browser's" function that giving a point x,y it returned the elements of an SVG that this point is over, it would make the visualization smoother and create "space" for more improvements. By the time I implemented it, I did not find such function.
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 have a series of nodes in a graph. The nodes are placed by the user in specific spots. The nodes are guaranteed to not overlap and, in fact, to have a buffer of space between them. These nodes are connected and each edge joins to a node at a specific point. I need to draw the edges between the nodes such that the edges:
(required) do not overlap the parent nodes
(ideally) would not overlap any node
I am not worried about edge crossings. Bonus points if there's an implementation of this in Javascript. I am unable to use any libraries outside of Javascript.
One solution could be using Bézier Curves:
"A Bézier curve is defined by a set of control points P0 through Pn,
where n is called its order (n = 1 for linear, 2 for quadratic, etc.).
The first and last control points are always the end points of the
curve; however, the intermediate control points (if any) generally do
not lie on the curve."
So the basic idea is to use parent node(s) as intermediate control points. You may also use points of the edges as intermediate control points to avoid edges overlapping.
In the wiki article you can find nice animations explaining it.
For javascript implementation I took a look at the followings libs:
jsdraw2d (LGPL license) with a nice demo and well referenced. Implemented it also using HTML5 and SVG for performance (jsdraw2dX).
jsbezier on google code
But if you google "javascript bezier library" you can find more.
If you are familiar with C# and .NET you can explore Microsoft.GLEE library (description is here and here) via ILSpy, or even theoretically save this sources to .csproj, modify and recompile it with Script# to JavaScript.
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.