How to create a “heatmap” graph network visualization

What we’re after

@CyberSiftIO we’ve been going through an exercise of adding “confidence levels” to our visualizations. In other words, how confident is the CyberSift engine that an alert really is an anomaly/outlier? The above screenshot shows one of the ways we visualize the output from this exercise. Each blue node is an internal PC/Server, while the other nodes are detected anomalies – ranging from green (low confidence) to red (high confidence). This heatmap-style visualization immediately allows an analyst to focus on those anomalies that really matter. Without the different colors, there may be too many alerts to investigate, but with the heatmap colors and analyst immediately figures out that best start looking at the deep orange alert on the top right corner. In this post we’ll outline how we built the below visualization.

WhatsApp Image 2017-10-16 at 16.54.21.jpeg


The toolset

Here’s the libraries we used:

  • ReactJS as our base framework (optional – in reality any JS framework could be used, we just like how easy and structured ReactJS makes everything)
  • CytoscapeJS as our graph network visualization library
  • D3.js for some helper functions

 


The coding

We’ll assume you have a basic ReactJS app up and running (if not… use create-react-app). The first order of the day is to format our data in a way that CytoscapeJS expects it. In this particular case, this means building an array of objects. Assuming our array of objects is going to be called cystoscape_elementswe first loop through the internal nodes (light blue ones) and push a data object onto this array:


internal_nodes = ["192.168.1.1","192.168.2.1"...] //sample data – in reality you'd probably get this from an API
cytoscape_elements = [];
internal_nodes.forEach(function(element){
cytoscape_elements.push({
data: {
id: element,
label: element,
backgroundColor: "internal"
}
});
});

view raw

graph_1.js

hosted with ❤ by GitHub

The most important thing to note in the above is that we add an “extra” object attribute named “backgroundColor” and set to “internal“, which we’ll later use for styling these nodes with the appropriate light blue color.


Next, we need to append our external nodes to the cytoscape_elements object. However unlike in our above code, these nodes need to be given a different color depending on their “confidence rating”. Let’s assume that the confidence rating can range from 0 (low outlier score) to 1 (high outlier score). We need to convert this range into a color palette. Fortunately, D3.js allows you to do exactly that, in a simple way:


import * as d3 from "d3";
let node_color_scale = d3.scaleLinear()
.domain([0, 0.5, 1])
.range([d3.rgb("#96f788"), d3.rgb('#f9f370'), d3.rgb('#f27676')]);

view raw

graph_2.js

hosted with ❤ by GitHub

In the above code, we defined a linear scale with a domain (possible number values) between 0 and 1. In the last line, we map the domain to a custom color range. The first RGB value is the start color which maps to the numerical value of “0”, the middle is the “pivot” value, and the last is the end color which maps to a numerical value of “1”.

Once we have this color scale, we can push external nodes to our array using a similar strategy as above:


external_nodes = [["1.1.1.1", 0.23],["2.2.2.2", 0.85],] //sample data – in reality you'd probably get this from an API
external_nodes.forEach(function(element){
const label = element[0];
const confidence = element[1];
cytoscape_elements.push({
data: {
id: label,
label: label,
confidence: node_color_scale(confidence)
}
});
});

view raw

graph_3.js

hosted with ❤ by GitHub

Note how  as before, we define a new object attribute called “confidence”, and we subsequently populate this attribute with our previously defined color scale to convert the numerical confidence to an actual RGB color. We’ll use this attribute later to style the node.


Next, we add the “edges” in the graph connecting the nodes together in a pretty straightforward fashion:


edges = [["192.168.1.1","1.1.1.1"],["192.168.2.1","2.2.2.2"],] //sample data – in reality you'd probably get this from an API
let id_counter = 0;
external_nodes.forEach(function(element){
const src = element[0];
const dst = element[1];
cytoscape_elements.push({
data: {
id: "edge_"+id_counter,
source: src,
target: dst
}
});
id_counter ++;
});

view raw

graph_4.js

hosted with ❤ by GitHub

Note the use of javascript type coercion (from int to string) as our id, and note the javascript scope of allowing the inner forEach function to increment the id_counter variable.


Last, we put it all together:


var cy = cytoscape({
container: document.getElementById('graphNetwork'), // container to render in
elements: cytoscape_elements, // list of graph elements
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': 'data(confidence)',
'label': 'data(label)',
'text-opacity': 0.5,
'font-size': "0.5em",
'font-weight': "bold"
}
},
{
selector: 'node[backgroundColor = "src"]',
style: {
'background-color': '#58cdfc',
'label': 'data(label)',
'text-opacity': 0.5,
'font-size': "1em"
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}
]
});

view raw

graph_5.js

hosted with ❤ by GitHub

In the above code, note the lines:

  • Line 12: CytoscapeJS uses a system of “selectors” which allow you to style different elements of the graph network. In this case, we’re interested in nodes.
  • Line 16: We set the background-color CSS attribute to the value of our “confidence” attribute in the data object (if present) using the notation below (NB: this would be my entire takeaway from this article…)
'data(attribute)'
  • Line 32: We again use selectors to style the internal nodes with a light blue. Recall that internal nodes had a data attribute of “backgroundColor” set to “src“, and we leverage this in the selector:
'node[backgroundColor = "src"]'

The above code in plain English says: select those nodes whose “backgroundColor is set to “src

 


Wrap Up

As you can see, the most important points in this article relate to how to effectively use CytoscapeJS custom data attributes along with CytoscapeJS selectors. Together, they allow you to create very striking visualizations that really communicate a point efficiently to your target audience.

Advertisement
Privacy Settings