How to manipulate cytoscape.js graph data before rendering - javascript

I have relatively big amount of graph data (over 1000 nodes, over 2000 edges). Cytoscape.js is failing to render this amount of data, so I decided I will cook some interactive graph exploration. I want to start with graph roots (it is DAG) and user will be able to explore nodes after clicking on them.
I know I can do it outside of cytoscape.js, but I wanted to know if I can create a node/edge collection that I would manipulate (remove all nodes but roots and their neighbours) and then use as data source (get child nodes of nodes). Cytoscape makes graph processing quite easy and it would be great to not need to reinvent the wheel.
I tried creating cy.collection based on the JSON data I was feeding to the graph, but it seems like it doesn't accept the same data format as cy.load.

I was able to work around that by creating two cytoscape objects. I am loading all my graph data to the variable I called allcy:
var allcy = cytoscape({
headless: true,
});
Notice that it's headless, so it won't try to render nodes in any way.
Then I am creating normal "working" cy var
var cy = cytoscape({
container: document.getElementById('thegraph'),
});
I load all my data to allcy
allcy.load(response); //response is json graph data I got from xmlhttp request
I add roots and their neighbourhood to cy and reload the layout.
cy.add(allcy.nodes().roots().closedNeighborhood());
cy.layout(layoutSettings);
Voila! I have only root nodes in my rendered cy graph. Now I need to add other nodes from allcy as needed, but I think I will manage to do it. Hope it comes in handy to someone.

Related

Layout DirectedGraph (dagre) only on a subset of nodes

I'm looking for a way to layout only a subset of nodes of a directed graph with JointJS / Rappid diagramming library.
I need some "fixed" nodes in the graph and layout the "others", assuming that they can be connected between each other or with some of the fixed nodes (the graph is already added into the paper).
Since the joint.layout.DirectedGraph.layout API must be used on a graph object, I was wondering if there is any mechanism to have some nodes of the graph "fixed" during the layout calculation (some proprety to add in the cell object, for example).
Also something like this can be fine, but no incoming and outgoing links should be retrieved by the getSubgraph API
var subGraph = graph.getSubgraph([A, B]);
joint.layout.DirectedGraph.layout(subGraph, layoutOpt);
Looking at the docs I was not able to identify this kind of feature.
If this feature is not supported, there is any other approach that I could use to achieve my goal? (Of course I can also layout the entire graph and apply my fixed chords when the operation ends, but I was looking for something better than this).
So I'm pretty sure that the auto layout functionality using Dagre is now only available in the Rappid version of joint js (i.e. paid for). What I do is use Dagre separately to perform the layout calculations and then iterate back through the elements and use the Dagre output to change their position manually. Not ideal, however it does allow you to do whatever you want in terms of only looking at a subset of nodes. Basic code below (graphObj is the jointjs graph object), you should be able to use this as a starting point if you wanted to work with a subset of elements and links:
var nodes = [];
var edges = [];
var elements = graphObj.getElements();
elements.forEach(function(element){
element.label = element.id;
element.width = element.attributes.size.width;
element.height = element.attributes.size.height;
});
var links = graphObj.getLinks();
links.forEach(function(link){
edges.push({source: link.getSourceElement(), target: link.getTargetElement()});
});
dagre.layout()
.nodeSep(150)
.edgeSep(100)
.rankSep(150)
.rankDir("LR")
.nodes(elements)
.edges(edges)
.run();
elements.forEach(function(element){
element.position(element.dagre.x, element.dagre.y);
element.attributes.prop.metadata.x = element.dagre.x;
element.attributes.prop.metadata.y = element.dagre.y;
});

vis.js graph not stabilizing even after hours

I have a network of around 1000 nodes. I have set stabilize:true and zoomExtentOnStabilize: true. The nodes are being added from JSON using vis.network.gephiParser.parseGephi() function. When I tried to plot this graph it never stabilizes even after hours of letting it idle. But then smaller number of nodes stabilize in reasonable time. What am I missing here. Is there any way to stabilize big graphs. I even tried setting the number of iterations to stabilize to 1000 and even higher. Thanks in advance for the help.
P.S.:The coordinates of the nodes are not available from JSON. The graph is redrawn based on the user input.
EDIT 1:
The JSON data being plotted is available at http://pastebin.com/raw.php?i=Mzy4ncxw. I couldn't make a reproducible example at jsbin because of CORS error.
The JavaScript code is:
message = JSON.parse(json_data); // json_data is sent from R server.
var nodes = new vis.DataSet();
var edges = new vis.DataSet();
var container = document.getElementById("div_graph");
var data = {
nodes: nodes,
edges: edges
};
var options = {
tooltip: {
delay: 50,
fontColor: "black",
fontSize: 14,
fontFace: "verdana",
color: {
border: "#666",
background: "#FFFFC6"
}
},
clustering: {
enabled: clusteringOn,
clusterEdgeThreshold: 50
},
hideEdgesOnDrag: true,
stabilize: true,
zoomExtentOnStabilize: true,
navigation: true,
keyboard: true,
edges: {
inheritColor: "to"
}
};
var network = new vis.Network(container, data, options);
nodes.clear();
edges.clear();
var parsed = vis.network.gephiParser.parseGephi(message);
nodes.add(parsed.nodes);
edges.add(parsed.edges);
network.redraw();
I'm the developer of the network module of visjs. we have used it to stabilize much larger sets than 1000 nodes. I can't really say what's going wrong here based on the information you supply. I'd like to invite you to make an issue on our github page. We try to collect all questions there. Can you share the code you use or your data (labels scrambled for anonymity ofcourse).
If I were to guess, a 1000 node system would stabilize with about 3000 iterations. If you are using dynamic smooth curves this increases greatly as support nodes are added to position the curves. I have used 15000 iterations for a 3000 node and 25000 edge system and even then it is not finished but I stop the simulation at that point regardless.
When you say redrawn on user input, is the data reloaded or redrawn in the sense that you see the dragging or zooming (similar to the redraw function)?
~ Alex
EDIT:
Based on your data I encoutered a few problems.
First, it seems you do not allow the nodes to move but also do not supply their positions, leading to an infinite recursion in the quadtree building process. I'll make the gephiParser more robust for this in the future.
See here for settings of the gephi parser: http://visjs.org/docs/network.html#Gephi_import
Secondly, You use dynamic smooth curves and a lot of interconnected nodes. Each smooth curve has an invisible support node that helps the positioning. This makes your system unstable (look at it with stabilize of to see the behaviour). In the v4 version you can set your own timestep to rectify this, but alternatively you can change your physics settings. Try the configurePhysics option and see if that helps. You can still use static smooth curves for aesthetic purposes.
To wrap up, I could get your system to stabilize with static smooth curves in about 3000 iterations, taking about a minute. I disabled clustering in your options. I'd recommend you wait for the 4.0 release to use clustering as it will be much much more powerful.
EDIT 2:
Here is a JSBin showing a working stabilization with your code and data (although modified)
http://jsbin.com/tiwijixoha/5/edit?html,output
So if you ment that it does not stabilize in the sense that it does not hide itself and only shows when it is ready instead of never reaching a stabilized state, then the problem is that stabilization is only done with a setData(), not with a dataset update.
In this jsbin I have also changed your edges and altered the physics to make it stable. You can play around with it a bit more if you're unhappy with it.

D3 graphing selective portions of data set

I have a large time series data set I need to graph, and am trying to use D3 to do it. I plan to have my graph have the x-axis be time, and allow for movement of the graph in the x direction. I want to have the graph only load/display the points that exist in the current time range on the screen.
For example, if my dataset has times 1-100, but the graph starts out with times 1-10 shown, the graph should only graph points 1-10. Then the user may move to the right and see times 5-15 and the graph should update accordingly.
Can anyone explain to me how this might be done via d3? I am having a hard time bridging the understanding from an entire data set being loaded in at once and graphed immediately to selective graphing of subsets of the data.
I think you are looking for the selection.filter() function. For example you can have:
var allNodes = vis.selectAll("Nodes").data(data.nodes);
var validNodes = allNodes.filter(function(d){return (d.time>1 && d.time <10)});
//use normal graph functions on validNodes.
You can also apply filter directly on the array of nodes.

arbor js - save and load graph

I'm using arbor js to create diagrams using its force-based alorithm.
I would like to be able to save the x,y system coordinates of the nodes after the total energy has got below a certain point, so that I can load them back in when accessing the graph, to avoid having to re-process all the energy from scratch.
I have had two problems doing this:
sys.energy().sum always returns NaN
as an alternative I decided to use setTimeout to save the graph node positions after a given period of time has elapsed
While I have been able to save the nodes x,y system coordinates to DB, when I try to load the data into the graph (using sys.merge or sys.addNode) with saved x y coordinates, the graph fails to display, and an 'out of memory' message appears in the console log.
Here is an example of data with node x,y system coordinates that were saved, and which causes arbor js to crash which loading:
{"nodes":{"0":{"edgeCount":0,"x":13.11901,"y":14.89151,"id":0,"name":"Global"},"23":
{"edgeCount":1,"x":18.08981,"y":-0.8355745,"id":23,"name":"Aristotle"},"26":
{"edgeCount":1,"x":5.688836,"y":14.86863,"id":26,"name":"Socrates"},"27":
{"edgeCount":1,"x":14.84461,"y":-1.687457,"id":27,"name":"Christianity"},"34":
{"edgeCount":1,"x":-2.265221,"y":13.59168,"id":34,"name":"Maths"},"91":
{"edgeCount":11,"x":5.868572,"y":-1.315289,"id":91,"name":"Plato"},"92":
{"edgeCount":4,"x":-3.14131,"y":8.81194,"id":92,"name":"Virtue"},"127":
{"edgeCount":1,"x":-3.308347,"y":8.463552,"id":127,"name":"Poetry"},"131":
{"edgeCount":1,"x":7.605158,"y":-3.951363,"id":131,"name":"Wisdom"},"147":
{"edgeCount":1,"x":6.195698,"y":-4.572639,"id":147,"name":"Person"},"149":
{"edgeCount":1,"x":2.10395,"y":-2.390886,"id":149,"name":"Republic"},"171":
{"edgeCount":1,"x":3.359434,"y":-3.996424,"id":171,"name":"Justice"},"172":
{"edgeCount":1,"x":-0.2544371,"y":-4.218832,"id":172,"name":"Temperance"},"173":
{"edgeCount":1,"x":-8.773163,"y":2.587845,"id":173,"name":"Courage"},"178":
{"edgeCount":1,"x":-2.380451,"y":-5.787674,"id":178,"name":"Schopenhauer"},"194":
{"edgeCount":2,"x":-7.747643,"y":-1.653015,"id":194,"name":"Beauty"},"195":
{"edgeCount":1,"x":-10.45985,"y":-2.670299,"id":195,"name":"Objective"},"196":
{"edgeCount":1,"x":-10.64573,"y":-6.874766,"id":196,"name":"Truth"},"219":
{"edgeCount":1,"x":-6.22574,"y":-7.565969,"id":219,"name":"Theory of forms"},"221":
{"edgeCount":1,"x":-8.314561,"y":-7.570002,"id":221,"name":"Platinus"},"245":
{"edgeCount":0,"x":-13.20351,"y":-8.421284,"id":245,"name":"Diagram"},"254":
{"edgeCount":1,"x":-13.54734,"y":-7.7437,"id":254,"name":"Green"}},
"edges":{"23":{"91":{"context":{"id":0,"name":"Global"},"source":
{"id":23,"name":"Aristotle"},"predicate":{"id":21,"name":"studied with"},"target":
{"id":91,"name":"Plato"}}},"26":{"91":{"context":{"id":0,"name":"Global"},"source":
{"id":26,"name":"Socrates"},"predicate":{"id":2,"name":"inspires"},"target":
{"id":91,"name":"Plato"}}},"91":{"149":{"context":{"id":0,"name":"Global"},"source":
{"id":91,"name":"Plato"},"predicate":{"id":1,"name":"writes"},"target":
{"id":149,"name":"Republic"}},"219":{"context":{"id":0,"name":"Global"},"source":
{"id":91,"name":"Plato"},"predicate":{"id":1,"name":"writes"},"target":{"id":219,"name":"Theory of forms"}},"27":{"context":{"id":0,"name":"Global"},"source":{"id":91,"name":"Plato"},"predicate":
{"id":3,"name":"influences"},"target":{"id":27,"name":"Christianity"}},"178":{"context":
{"id":0,"name":"Global"},"source":{"id":91,"name":"Plato"},"predicate":
{"id":3,"name":"influences"},"target":{"id":178,"name":"Schopenhauer"}},"221":{"context":
{"id":0,"name":"Global"},"source":{"id":91,"name":"Plato"},"predicate":
{"id":3,"name":"influences"},"target":{"id":221,"name":"Platinus"}},"254":{"context":
{"id":245,"name":"Diagram"},"source":{"id":91,"name":"Plato"},"predicate":
{"id":28,"name":"is"},"target":{"id":254,"name":"Green"}},"34":{"context":
{"id":0,"name":"Global"},"source":{"id":91,"name":"Plato"},"predicate":{"id":33,"name":"is associated with"},"target":{"id":34,"name":"Maths"}},"127":{"context":
{"id":0,"name":"Global"},"source":{"id":91,"name":"Plato"},"predicate":{"id":47,"name":"is obsessed with"},"target":{"id":127,"name":"Poetry"}},"147":{"context":
{"id":0,"name":"Global"},"source":{"id":91,"name":"Plato"},"predicate":{"id":56,"name":"is type of"},"target":{"id":147,"name":"Person"}}},"92":{"131":{"context":{"id":91,"name":"Plato"},"source":
{"id":92,"name":"Virtue"},"predicate":{"id":28,"name":"is"},"target":
{"id":131,"name":"Wisdom"}},"171":{"context":{"id":91,"name":"Plato"},"source":
{"id":92,"name":"Virtue"},"predicate":{"id":28,"name":"is"},"target":
{"id":171,"name":"Justice"}},"172":{"context":{"id":91,"name":"Plato"},"source":
{"id":92,"name":"Virtue"},"predicate":{"id":28,"name":"is"},"target":
{"id":172,"name":"Temperance"}},"173":{"context":{"id":91,"name":"Plato"},"source":
{"id":92,"name":"Virtue"},"predicate":{"id":28,"name":"is"},"target":
{"id":173,"name":"Courage"}}},"194":{"195":{"context":{"id":91,"name":"Plato"},"source":
{"id":194,"name":"Beauty"},"predicate":{"id":28,"name":"is"},"target":
{"id":195,"name":"Objective"}},"196":{"context":{"id":91,"name":"Plato"},"source":
{"id":194,"name":"Beauty"},"predicate":{"id":33,"name":"is associated with"},"target":
{"id":196,"name":"Truth"}}}}}
I can find no examples of graphs where the node x,y coordinates are in the input data.
Looking at https://github.com/samizdatco/arbor/blob/master/src/physics/system.js#L83 it seems that it should be alright to include x and y value in the data dictionary. I'm not really sure from your example how you pass the data to the methods. Maybe try just adding a few of the nodes and see where they are placed. I have the same problem with the energy being NaN. Probably using a debugger will help.

D3 - partly force directed graph

As I'm a beginner, here's another question about D3.js.
I have constructed a force directed graph, just as a lot did.
All the nodes are stored in one array and all links in another.
Now I want the central node to be fixed in the middle of the svg and the others lingering around it.
Is there any possibility to cut only one node out of the force layout without it falling out of my net? Has anybody already made it?
You can set the fixed property of a node to true, then place it where you like - see this related question.
// explicity set node position
var fixedNode = graph.nodes[0];
fixedNode.fixed = true;
fixedNode.x = width/2;
fixedNode.y = height/2;
Working fiddle: http://jsfiddle.net/nrabinowitz/z2cye/

Categories

Resources