This notebook demonstrates the visualization of subgraphs from the Neo4j Graph Database. It uses the py2neo library to access a Neo4j database instance.
The examples in this notebook access the COVID-19-Net Knowledge Graph.
NOTE, this notebook does not run on MyBinder.org due to a blocked port.
However, you can run this and other example notebooks on Pangeo Binder here.
Author: Peter W. Rose (pwrose@ucsd.edu)
import random
import ipycytoscape
from py2neo import Graph
node_centered = {'selector': 'node',
'style': {'font-size': '10',
'label': 'data(name)',
'height': '60',
'width': '80',
'text-max-width': '80',
'text-wrap': 'wrap',
'text-valign': 'center',
'background-color': 'blue',
'background-opacity': 0.6}
}
edge_directed = {'selector': 'edge',
'style': {'line-color': '#9dbaea',
'target-arrow-shape': 'triangle',
'target-arrow-color': '#9dbaea',
'curve-style': 'bezier'}
}
edge_directed_named = {'selector': 'edge',
'style': {'font-size': '8',
'label': 'data(name)',
'line-color': '#9dbaea',
'text-rotation': 'autorotate',
'target-arrow-shape': 'triangle',
'target-arrow-color': '#9dbaea',
'curve-style': 'bezier'}
}
edge_undirected = {'selector': 'edge',
'style': {'line-color': '#9dbaea'}
}
Change seed to select a different color palette.
def random_color_palette(n_colors, seed=3):
"""
Creates a random color palette of n_colors.
Parameters
----------
n_colors : int
number of colors in the color palette
seed : int
random number seed
See https://stackoverflow.com/questions/28999287/generate-random-colors-rgb)
"""
random.seed(seed)
return ['#'+''.join([random.choice('0123456789ABCDEF') for j in range(6)]) for i in range(n_colors)]
Here we use the COVID-19-Net Knowledge Graph to demonstrate how to run a Neo4j Cypher query and pass the resulting subgraph into Cytoscape.
graph = Graph("bolt://132.249.238.185:7687", user="reader", password="demo")
This query demonstrates the geographic hierachy in COVID-19-Net.
query1 = """
MATCH p=(:City{name:'San Francisco'})-[:IN*]->(:World) RETURN p
"""
subgraph1 = graph.run(query1).to_subgraph()
We create a tooltip
named 'tooltip' and use this attribute later to create a tool tip.
widget1 = ipycytoscape.CytoscapeWidget()
widget1.graph.add_graph_from_neo4j(subgraph1)
style1 = [node_centered, edge_directed]
labels1 = list(subgraph1.labels())
labels1.sort()
print('Node labels:', labels1)
Node labels: ['Admin1', 'Admin2', 'City', 'Country', 'Location', 'UNIntermediateRegion', 'UNRegion', 'UNSubRegion', 'USDivision', 'USRegion', 'World']
colors1 = random_color_palette(len(labels1))
for label, color in zip(labels1, colors1):
style1.append({'selector': 'node[label = "' + label + '"]', 'style': {'background-color': color}})
widget1.set_style(style1)
widget1.set_layout(name='dagre', padding=0)
When a Neo4j subgraph is added to a Cytoscape graph, a tooltip
attribute is generated that contains all Neo4j node properties.
widget1.set_tooltip_source('tooltip')
Click on a node to show the tooltip
widget1
CytoscapeWidget(cytoscape_layout={'name': 'dagre', 'padding': 0}, cytoscape_style=[{'selector': 'node', 'style…
Here we run an undirected query (no "->" arrow) since the direction of interaction is arbitrary.
query2 = """
MATCH p=(:Protein{name: 'Spike glycoprotein', taxonomyId: 'taxonomy:2697049'})-[:INTERACTS_WITH]-(:Protein) RETURN p
"""
subgraph2 = graph.run(query2).to_subgraph()
query2 = """
MATCH p=(:Protein{name: 'Angiotensin-converting enzyme 2', taxonomyId: 'taxonomy:9606'})-[:INTERACTS_WITH]-(:Protein) RETURN p
"""
subgraph2 = graph.run(query2).to_subgraph()
widget2 = ipycytoscape.CytoscapeWidget()
widget2.graph.add_graph_from_neo4j(subgraph2)
style2 = [node_centered, edge_undirected]
labels2 = list(subgraph2.labels())
labels2.sort()
print('Node labels:', labels2)
Node labels: ['Protein']
colors2 = random_color_palette(len(labels2))
for label, color in zip(labels2, colors2):
style2.append({'selector': 'node[label = "' + label + '"]', 'style': {'background-color': color}})
widget2.set_style(style2)
widget2.set_layout(name='concentric', padding=0)
widget2.set_tooltip_source('tooltip')
Click on a node to show the tooltip
TODO: The rendering of this concentric network has a rendering error. This must be a cytoscape issue.
widget2
CytoscapeWidget(cytoscape_layout={'name': 'concentric', 'padding': 0}, cytoscape_style=[{'selector': 'node', '…
query3 = """
MATCH p=(:MetaNode)-[:ETL_FROM]->(:DataSource) RETURN p // ETL_FROM: Extracted, transformed, and loaded FROM
"""
subgraph3 = graph.run(query3).to_subgraph()
widget3 = ipycytoscape.CytoscapeWidget()
widget3.graph.add_graph_from_neo4j(subgraph3)
style3 = [node_centered, edge_directed]
labels3 = list(subgraph3.labels())
labels3.sort()
print('Node labels:', labels3)
Node labels: ['DataSource', 'MetaNode']
colors3 = random_color_palette(len(labels3))
for label, color in zip(labels3, colors3):
style3.append({'selector': 'node[label = "' + label + '"]', 'style': {'background-color': color}})
widget3.set_style(style3)
widget3.set_layout(name='klay', padding=0)
widget3.set_tooltip_source('tooltip')
Click on a node to show the tooltip
widget3
CytoscapeWidget(cytoscape_layout={'name': 'klay', 'padding': 0}, cytoscape_style=[{'selector': 'node', 'style'…
query4 = """
MATCH p=(a:MetaNode)-[:META_RELATIONSHIP]->(b:MetaNode)
WHERE a.name <> 'Location' AND b.name <> 'Location' // exclude Location nodes since they make the graph too crowded
RETURN p
"""
subgraph4 = graph.run(query4).to_subgraph()
widget4 = ipycytoscape.CytoscapeWidget()
widget4.graph.add_graph_from_neo4j(subgraph4)
style4 = [node_centered, edge_directed_named]
labels4 = list(subgraph4.labels())
labels4.sort()
print('Node labels:', labels4)
Node labels: ['MetaNode']
colors4 = random_color_palette(len(labels4))
for label, color in zip(labels4, colors4):
style4.append({'selector': 'node[label = "' + label + '"]', 'style': {'background-color': color}})
widget4.set_style(style4)
Cola layout options
widget4.set_layout(name='cola', padding=0, nodeSpacing=65, nodeDimensionsIncludeLabels=True, unconstrIter=5000)
widget4.set_tooltip_source('tooltip')
Click on a node to show the tooltip
widget4
CytoscapeWidget(cytoscape_layout={'name': 'cola', 'padding': 0, 'nodeSpacing': 65, 'nodeDimensionsIncludeLabel…