{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Neo4j Graph\n", "This notebook demonstrates the visualization of subgraphs from the [Neo4j](https://neo4j.com/) Graph Database. It uses the [py2neo](https://py2neo.org/) library to access a Neo4j database instance.\n", "\n", "The examples in this notebook access the [COVID-19-Net](https://github.com/covid-19-net/covid-19-community) Knowledge Graph.\n", "\n", "**NOTE, this notebook does not run on MyBinder.org due to a blocked port.**\n", "\n", "**However, you can run this and other example notebooks on Pangeo Binder [here](https://github.com/sbl-sdsc/neo4j-ipycytoscape).**\n", "\n", "Author: Peter W. Rose (pwrose@ucsd.edu)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import random\n", "import ipycytoscape\n", "from py2neo import Graph" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Node and edge styles" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "node_centered = {'selector': 'node',\n", " 'style': {'font-size': '10',\n", " 'label': 'data(name)',\n", " 'height': '60',\n", " 'width': '80',\n", " 'text-max-width': '80',\n", " 'text-wrap': 'wrap',\n", " 'text-valign': 'center',\n", " 'background-color': 'blue',\n", " 'background-opacity': 0.6}\n", " }" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "edge_directed = {'selector': 'edge',\n", " 'style': {'line-color': '#9dbaea',\n", " 'target-arrow-shape': 'triangle',\n", " 'target-arrow-color': '#9dbaea',\n", " 'curve-style': 'bezier'}\n", " }" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "edge_directed_named = {'selector': 'edge',\n", " 'style': {'font-size': '8',\n", " 'label': 'data(name)',\n", " 'line-color': '#9dbaea',\n", " 'text-rotation': 'autorotate',\n", " 'target-arrow-shape': 'triangle',\n", " 'target-arrow-color': '#9dbaea',\n", " 'curve-style': 'bezier'}\n", " }" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "edge_undirected = {'selector': 'edge',\n", " 'style': {'line-color': '#9dbaea'}\n", " }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Node colors\n", "Change seed to select a different color palette." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def random_color_palette(n_colors, seed=3):\n", " \"\"\" \n", " Creates a random color palette of n_colors.\n", " \n", " Parameters\n", " ----------\n", " n_colors : int\n", " number of colors in the color palette\n", " seed : int\n", " random number seed\n", "\n", " See https://stackoverflow.com/questions/28999287/generate-random-colors-rgb)\n", " \"\"\"\n", " random.seed(seed)\n", " return ['#'+''.join([random.choice('0123456789ABCDEF') for j in range(6)]) for i in range(n_colors)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Connect to a Neo4j Database\n", "Here we use the [COVID-19-Net](https://github.com/covid-19-net/covid-19-community) Knowledge Graph to demonstrate how to run a Neo4j Cypher query and pass the resulting subgraph into Cytoscape." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "graph = Graph(\"bolt://132.249.238.185:7687\", user=\"reader\", password=\"demo\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example 1: Find all cities with the name \"San Francisco\"\n", "This query demonstrates the geographic hierachy in COVID-19-Net." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "query1 = \"\"\"\n", "MATCH p=(:City{name:'San Francisco'})-[:IN*]->(:World) RETURN p\n", "\"\"\"\n", "subgraph1 = graph.run(query1).to_subgraph()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Add Neo4j subgraph to Cytoscape Widget\n", "We create a `tooltip` named 'tooltip' and use this attribute later to create a tool tip." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "widget1 = ipycytoscape.CytoscapeWidget()\n", "widget1.graph.add_graph_from_neo4j(subgraph1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Set node and edge styles" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "style1 = [node_centered, edge_directed]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Set node specific colors based on node labels" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Node labels: ['Admin1', 'Admin2', 'City', 'Country', 'Location', 'UNIntermediateRegion', 'UNRegion', 'UNSubRegion', 'USDivision', 'USRegion', 'World']\n" ] } ], "source": [ "labels1 = list(subgraph1.labels())\n", "labels1.sort()\n", "print('Node labels:', labels1)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "colors1 = random_color_palette(len(labels1))" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "for label, color in zip(labels1, colors1):\n", " style1.append({'selector': 'node[label = \"' + label + '\"]', 'style': {'background-color': color}})" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "widget1.set_style(style1)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "widget1.set_layout(name='dagre', padding=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When a Neo4j subgraph is added to a Cytoscape graph, a ```tooltip``` attribute is generated that contains all Neo4j node properties." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "widget1.set_tooltip_source('tooltip')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Click on a node to show the tooltip" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "2c0b5deefa60478b9061dd1d051e8b53", "version_major": 2, "version_minor": 0 }, "text/plain": [ "CytoscapeWidget(cytoscape_layout={'name': 'dagre', 'padding': 0}, cytoscape_style=[{'selector': 'node', 'style…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "widget1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example 2: Find all proteins that interact with the SARS-CoV-2 Spike protein\n", "Here we run an undirected query (no \"->\" arrow) since the direction of interaction is arbitrary." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "query2 = \"\"\"\n", "MATCH p=(:Protein{name: 'Spike glycoprotein', taxonomyId: 'taxonomy:2697049'})-[:INTERACTS_WITH]-(:Protein) RETURN p\n", "\"\"\"\n", "subgraph2 = graph.run(query2).to_subgraph()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "query2 = \"\"\"\n", "MATCH p=(:Protein{name: 'Angiotensin-converting enzyme 2', taxonomyId: 'taxonomy:9606'})-[:INTERACTS_WITH]-(:Protein) RETURN p\n", "\"\"\"\n", "subgraph2 = graph.run(query2).to_subgraph()" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "widget2 = ipycytoscape.CytoscapeWidget()\n", "widget2.graph.add_graph_from_neo4j(subgraph2)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "style2 = [node_centered, edge_undirected]" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Node labels: ['Protein']\n" ] } ], "source": [ "labels2 = list(subgraph2.labels())\n", "labels2.sort()\n", "print('Node labels:', labels2)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "colors2 = random_color_palette(len(labels2))" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "for label, color in zip(labels2, colors2):\n", " style2.append({'selector': 'node[label = \"' + label + '\"]', 'style': {'background-color': color}})" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "widget2.set_style(style2)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "widget2.set_layout(name='concentric', padding=0)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "widget2.set_tooltip_source('tooltip')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Click on a node to show the tooltip" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "TODO: The rendering of this concentric network has a rendering error. This must be a cytoscape issue." ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "380a3f2e48ad4215b9b111ff35603f60", "version_major": 2, "version_minor": 0 }, "text/plain": [ "CytoscapeWidget(cytoscape_layout={'name': 'concentric', 'padding': 0}, cytoscape_style=[{'selector': 'node', '…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "widget2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example 3: Explore the Data Sources used to create the COVID-19-Net Knowledge Graph" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "query3 = \"\"\"\n", "MATCH p=(:MetaNode)-[:ETL_FROM]->(:DataSource) RETURN p // ETL_FROM: Extracted, transformed, and loaded FROM\n", "\"\"\"\n", "subgraph3 = graph.run(query3).to_subgraph()" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "widget3 = ipycytoscape.CytoscapeWidget()\n", "widget3.graph.add_graph_from_neo4j(subgraph3)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "style3 = [node_centered, edge_directed]" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Node labels: ['DataSource', 'MetaNode']\n" ] } ], "source": [ "labels3 = list(subgraph3.labels())\n", "labels3.sort()\n", "print('Node labels:', labels3)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "colors3 = random_color_palette(len(labels3))" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "for label, color in zip(labels3, colors3):\n", " style3.append({'selector': 'node[label = \"' + label + '\"]', 'style': {'background-color': color}})" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "widget3.set_style(style3)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "widget3.set_layout(name='klay', padding=0)" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "widget3.set_tooltip_source('tooltip')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Click on a node to show the tooltip" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "7a6e64b8e49e4876afe29bd7f424a2aa", "version_major": 2, "version_minor": 0 }, "text/plain": [ "CytoscapeWidget(cytoscape_layout={'name': 'klay', 'padding': 0}, cytoscape_style=[{'selector': 'node', 'style'…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "widget3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example 4: Create a Metagraph that shows the Nodes and their Relationships in the COVID-19-Net Knowledge Graph" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "query4 = \"\"\"\n", "MATCH p=(a:MetaNode)-[:META_RELATIONSHIP]->(b:MetaNode) \n", "WHERE a.name <> 'Location' AND b.name <> 'Location' // exclude Location nodes since they make the graph too crowded\n", "RETURN p\n", "\"\"\"\n", "subgraph4 = graph.run(query4).to_subgraph()" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "widget4 = ipycytoscape.CytoscapeWidget()\n", "widget4.graph.add_graph_from_neo4j(subgraph4)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "style4 = [node_centered, edge_directed_named]" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Node labels: ['MetaNode']\n" ] } ], "source": [ "labels4 = list(subgraph4.labels())\n", "labels4.sort()\n", "print('Node labels:', labels4)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "colors4 = random_color_palette(len(labels4))" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "for label, color in zip(labels4, colors4):\n", " style4.append({'selector': 'node[label = \"' + label + '\"]', 'style': {'background-color': color}})" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "widget4.set_style(style4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cola layout [options](https://github.com/cytoscape/cytoscape.js-cola#api)" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [], "source": [ "widget4.set_layout(name='cola', padding=0, nodeSpacing=65, nodeDimensionsIncludeLabels=True, unconstrIter=5000)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "widget4.set_tooltip_source('tooltip')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Click on a node to show the tooltip" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "ea682a8625164da48eb2c887a017264d", "version_major": 2, "version_minor": 0 }, "text/plain": [ "CytoscapeWidget(cytoscape_layout={'name': 'cola', 'padding': 0, 'nodeSpacing': 65, 'nodeDimensionsIncludeLabel…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "widget4" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.2" } }, "nbformat": 4, "nbformat_minor": 4 }