RCyjs 2.10.0
Network (graph) visualization is useful for exploring relationships among entities, in biology as in other disciplines. cytoscape.js - part of the “Cytoscape ecosystem” of network vizualization and analysis tools - runs in all web browsers; RCyjs uses JSON messages over websockets to communicate between cytoscape.js and R, and all operations are provided as R function calls supplemented by interactive (mouse) operations in the browser.
This web-based implementation is related to, and can exchange data with the desktop version of Cytoscape: , which has its own Bioconductor package, RCy3. RCyjs requires no external software beyond your web browser. RCy3, in contrast, and in order to provide access to the broad capabilities of desktop Cytoscape, requires its installation on your computer. Both approaches are useful.
Following standard Bioconductor practice, full support is offered here for the core Bioconductor representation of network data: the graphNEL (node edge list) class. graphNELs support an number of node and edge attributes on a graph, along with easy conversion from graphAM (adjacency matrix) objects. Two alternative representations of graphs are indirectly supported at present, with direct support coming with the next release of RCyjs in autumn 2018: that of the popular igraph package, and the new data.frame representation used by the Cytoscape Consortium in their updates to the RCy3 package. But at present, users of these representations must transform their data into graphNELs: see igraph.to.graphNEL in the igraph package.
One weakness of the graphNEL class, reflecting its origins in graph theory, is that at most one edge can exist between any two nodes. In common biological practice - and in the cytoscape ecosystem - multiple edges between a pair of nodes, in the same direction, can be useful. This weakness will be addressed in the next release of the package.
Though a graph is fundamentally a static data structure, it can be used - and RCyjs encourages its use - as a represention of dynamic processes. A few techniuqes enable this:
Relations implicit in your data are thus made visible. We demonstrate these techniques with a small two-node example.
The names you choose for nodes become their reference identifiers - their id. These character strings are the names by which they are subsequently manipulated and queried. Visual mapping rules, however, allow you to use other names for display purposes. Here “A” and “T” is compact, easy to use, but we later set a “label” attribute on the nodes (“Activator” and “Target”) and optionally use that in the display.
nodes <- c("A", "T")
g <- graphNEL(nodes, edgemode="directed")
g <- graph::addEdge("A", "T", g)
Attributes are universal: every node has all the node attributes, every edge has all the edge attributes. This requirement is easily met by setting defaults:
nodeDataDefaults(g, attr="label") <- "undefined"
nodeDataDefaults(g, attr="type") <- "undefined"
nodeDataDefaults(g, attr="flux") <- 0
edgeDataDefaults(g, attr="edgeType") <- "undefined"
These attributes will remain fixed. Node flux will change.
nodeData(g, nodes, attr="label") <- c("Activator", "Target")
nodeData(g, nodes, attr="type") <- c("ligand", "receptor")
edgeData(g, "A", "T", attr="edgeType") <- "binds"
We provide three methods to control the visual appearance of nodes and edges in the graph:
RCyjs provides a minimal style via the function setDefaultStyle which we use to get started. We now create an RCyjs instance, load the graph, and create a minimal display, after which the three mapping methods are presented.
rcy <- RCyjs(title="RCyjs vignette")
setGraph(rcy, g)
layout(rcy, "grid")
fit(rcy, padding=200)
setDefaultStyle(rcy)
cytoscape.js ships with seventeen node shapes, and ten “edge decorators” - the small symbols (arrows, tee) which can be placed at the ends of edges connecting the nodes. Edges may be “solid”, “dotted” or “dashed”.
getSupportedNodeShapes(rcy)
getSupportedEdgeDecoratorShapes(rcy)
These setting then apply equally to all nodes and all edges. For an exhaustive inventory and examples of these methods, see _test_setDefaultStyleElements() in system.file(package=“RCyjs”, “unitTests”, “test_RCyjs.R”)
setDefaultNodeShape(rcy, "roundrectangle")
setDefaultNodeSize(rcy, 50)
setDefaultNodeColor(rcy, "lightgreen")
setDefaultNodeFontSize(rcy, 8)
setDefaultNodeFontColor(rcy, "darkgreen")
setDefaultEdgeLineStyle(rcy, "dotted")
setDefaultEdgeWidth(rcy, 1)
setDefaultEdgeLineColor(rcy, "black")
setDefaultNodeBorderWidth(rcy, 2)
setDefaultNodeBorderColor(rcy, "white")
setBackgroundColor(rcy, "#F0F0F0")
setDefaultEdgeTargetArrowShape(rcy, "arrow")
redraw(rcy)
Similar operations upon specific edges are not yet available - but are easily controlled via a style file as explained in the next section.
setNodeColor(rcy, "A", "lightblue")
setNodeSize(rcy, "T", 80)
setNodeShape(rcy, "A", "hexagon")
setNodeBorderWidth(rcy, "A", 3)
redraw(rcy)
setDefaultStyle(rcy)
In the two-node example given below we animate the display by successively updating the flux parameter on each node, simulating (in the most basic form) the activity of the target based upon the activity of the activator. We begin by specifying rules which map node color and node size to the current value of flux. These rules are contained in the extdata/vignetteStyle.js file included in the package, whose nature we introduce here by quoting the node style rules.
Note that rules for ALL nodes are presented first, followed by specialized color and shape rules for nodes of different types (activator nodes and target nodes). Note especially the mapData functions: they calculate visual attributes from the current value of the specified data attributes. Both nodes share the same size (width and height) rule, and different color rules.
These rules are invoked and applied by RCyjs every time you call the redraw method.
{selector: "node", css: {
"width": "mapData(flux, 0, 1000, 20, 100)",
"height": "mapData(flux, 0, 1000, 20, 100)",
"shape": "ellipse",
"text-halign": "center",
"text-valign": "center",
"background-color": "lightblue",
"color": "black",
"font-size": 12,
"border-width": 1,
"border-color": "lightgray",
"content": "data(id)"
}},
{selector:"node[type='activator']", css: {
"shape": "diamond",
"background-color": "mapData(flux, 0, 1000, white, red)",
}},
{selector:"node[type='target']", css: {
"shape": "roundrectangle",
"background-color":"mapData(flux, 0, 1000, white, darkgreen)",
}},
{selector:"node:selected", css: {
"overlay-opacity": 0.5,
}},
{selector: 'edge:selected', css: {
"overlay-opacity": 0.2,
}},
Before running the example code, let’s discuss the several options RCyjs provides for node placement - arguably the most important aspect of graph visualization:
For the currently displayed graph, these layout strategies are always available, written into the cytoscape.js code. Their use here is not visually interesting: our graph is too small. We conclude be resetting the basic grid layout which sets us up for subsequence code chunks later in this vignette.
for(layout.name in getLayoutStrategies(rcy)){
layout(rcy, layout.name)
Sys.sleep(1)
}
layout(rcy, "grid")
fit(rcy, padding=150)
RCyjs provides two methods: getLayout and setLayout. These return and consume, respectively, a data.frame with an id, x and y column. Their use is demonstrated in the example below to perform a simple animation. They can also be used if you wish to create your own layout algorithm.
This traditional direct manipulation of nodes needs little introduction: simply click on a node, or drag a box around multiple nodes, then drag them to their new location. You can use this approach for all the nodes in a graph but more plausibly you will find it useful following your use of an algorithmic layout, to fine-tune or correct its deficiencies. Once you have a layout you like and wish to keep, these two methods will be useful:
saveLayout(rcy, filename)
restoreLayout(rcy, filename)
RCyjs saves the layout as a data.frame in a compact RData format ready to be reused again later.
RCyjs provides a utility function which creates a small 3-node, 3-edge graphNEL with several node and edge attributes. We use this example to introduce simple attribute inspection:
library(RCyjs)
gDemo <- simpleDemoGraph()
noaNames(gDemo)
edaNames(gDemo)
noa(gDemo, "type")
noa(gDemo, "lfc")
eda(gDemo, "edgeType")
eda(gDemo, "score")
This toy 2-node network illustrates all the principles and operations explained above - all of which scale easily to more realistic networks and their visualization.
sessionInfo()