Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
395 changes: 395 additions & 0 deletions examples/nodes_2025_demo.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,395 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "8dcd267e",
"metadata": {},
"source": [
"# NODES 2025 Demo\n",
"\n",
"This notebook represents the demo part of the [presentation](https://neo4j.com/nodes-2025/agenda/easy-jupyter-notebook-graph-visualization-in-10-minutes/) at the NODES 2025 conference."
]
},
{
"cell_type": "markdown",
"id": "e15fcce5",
"metadata": {},
"source": [
"## Setup"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fb45b182",
"metadata": {},
"outputs": [],
"source": [
"%pip install \"neo4j-viz[gds, neo4j]\" python-dotenv"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10b32405",
"metadata": {},
"outputs": [],
"source": [
"from dotenv import load_dotenv\n",
"\n",
"# Load credentials for the neo4j database\n",
"load_dotenv(\"db_creds.env\")"
]
},
{
"cell_type": "markdown",
"id": "cfb884ec",
"metadata": {},
"source": [
"## Building our visualization graph\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "02304ee4",
"metadata": {},
"source": [
"### From a Create Query string\n",
"\n",
"The most simple way to test out neo4j-viz is by using `from_gql_create`.\n",
"The nodes and relationships are directly parsed from the provided query string. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "05eda814",
"metadata": {},
"outputs": [],
"source": [
"from neo4j_viz.gql_create import from_gql_create\n",
"\n",
"VG = from_gql_create(\"\"\"\n",
" CREATE\n",
" (alice:Person {name: 'Alice', age: 30}),\n",
" (bob:Person {name: 'Bob', age: 25}),\n",
" (carol:Person {name: 'Carol', age: 27}),\n",
" (alice)-[:FRIENDS_WITH {since: 2015}]->(bob),\n",
" (bob)-[:FRIENDS_WITH {since: 2018}]->(carol)\n",
"\"\"\")\n",
"\n",
"VG.render(initial_zoom=1.5)"
]
},
{
"cell_type": "markdown",
"id": "32979c03",
"metadata": {},
"source": [
"## From a Neo4j database\n",
"\n",
"Now lets assume, you have a Neo4j database available which you want to inspect.\n",
"In the following, I assume the Movies dataset is imported. You can use `:play movies` in the Neo4j Browser to import the dataset. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "72b14cea",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import neo4j\n",
"from neo4j_viz.neo4j import from_neo4j\n",
"\n",
"driver = neo4j.GraphDatabase.driver(\n",
" uri=os.getenv(\"NEO4J_URI\"),\n",
" auth=(os.getenv(\"NEO4J_USERNAME\"), os.getenv(\"NEO4J_PASSWORD\")),\n",
")\n",
"\n",
"# Limiting to 20 rows for demo purposes\n",
"VG = from_neo4j(driver, row_limit=20)\n",
"VG.render(initial_zoom=1.0)"
]
},
{
"cell_type": "markdown",
"id": "8c7091b4",
"metadata": {},
"source": [
"## From GDS"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "153decdb",
"metadata": {},
"outputs": [],
"source": [
"from graphdatascience import GraphDataScience\n",
"\n",
"gds = GraphDataScience(\n",
" endpoint=os.getenv(\"NEO4J_URI\"),\n",
" auth=(os.getenv(\"NEO4J_USERNAME\"), os.getenv(\"NEO4J_PASSWORD\")),\n",
")\n",
"gds.set_database(\"neo4j\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4c7cdcb4",
"metadata": {},
"outputs": [],
"source": [
"G, _ = gds.graph.cypher.project(\n",
" query=\"\"\"\n",
" MATCH (s:Person)-[:ACTED_IN]->(m:Movie)<-[:ACTED_IN]-(t:Person)\n",
" WITH s, t, count(m) as common_movies\n",
" WHERE s < t\n",
" RETURN gds.graph.project('demo-graph', s, t, {\n",
" sourceNodeLabels: labels(s),\n",
" targetNodeLabels: labels(t),\n",
" relationshipProperties: {weight: common_movies},\n",
" relationshipType: 'CO_ACTED'\n",
" }, {undirectedRelationshipTypes: ['CO_ACTED']})\n",
"\"\"\"\n",
")\n",
"\n",
"str(G)"
]
},
{
"cell_type": "markdown",
"id": "c5652cd6",
"metadata": {},
"source": [
"How do we make sure the projection matches our expectation? Especially if the Cypher queries get more complex"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d1b405bd",
"metadata": {},
"outputs": [],
"source": [
"from neo4j_viz.gds import from_gds\n",
"\n",
"# Limit to a couple of nodes to inspect the projection. Sampling via random-walks implemented in GDS\n",
"VG = from_gds(gds, G, db_node_properties=[\"name\"], max_node_count=20)\n",
"VG.render()"
]
},
{
"cell_type": "markdown",
"id": "eabdc19f",
"metadata": {},
"source": [
"Lets run some GDS algorithms to get some more insights about our co-actor graph."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7bfd2bc6",
"metadata": {},
"outputs": [],
"source": [
"gds.pageRank.mutate(G, relationshipWeightProperty=\"weight\", mutateProperty=\"pageRank\")\n",
"gds.leiden.mutate(G, mutateProperty=\"component\", relationshipWeightProperty=\"weight\")"
]
},
{
"cell_type": "markdown",
"id": "331f7f79",
"metadata": {},
"source": [
"Other supported datasources include `from_snowflake`, and `from_pandas`."
]
},
{
"cell_type": "markdown",
"id": "4c7da83d",
"metadata": {},
"source": [
"## Customizing the Visualization"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8c3e5d7a",
"metadata": {},
"outputs": [],
"source": [
"from neo4j_viz.gds import from_gds\n",
"\n",
"# make sure to include also the newly computed properties such as pageRank\n",
"# by default from_gds includes all properties of G\n",
"VG = from_gds(gds, G, db_node_properties=[\"name\"])\n",
"VG.render()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c5fc8746",
"metadata": {},
"outputs": [],
"source": [
"# Lets first fix the caption of the nodes to show the name.\n",
"for node in VG.nodes:\n",
" node.caption = node.properties.get(\"name\")\n",
"\n",
"VG.render()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5bb837b7",
"metadata": {},
"outputs": [],
"source": [
"# Inspect the computed communities to see which actors are grouped together\n",
"VG.color_nodes(property=\"component\", override=True)\n",
"VG.render()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5c6839b0",
"metadata": {},
"outputs": [],
"source": [
"# Resize nodes based on pageRank property, i.e., more important actors appear larger\n",
"VG.resize_nodes(property=\"pageRank\")\n",
"VG.render()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d8f4e4f0",
"metadata": {},
"outputs": [],
"source": [
"# To make sure certain nodes are always visible, we can pin them. Here we pin \"Keanu Reeves\".\n",
"pinned_nodes = {\n",
" node.id: True\n",
" for node in VG.nodes\n",
" if node.properties.get(\"name\") in [\"Keanu Reeves\"]\n",
"}\n",
"\n",
"VG.toggle_nodes_pinned(pinned_nodes)\n",
"VG.render()"
]
},
{
"cell_type": "markdown",
"id": "4e565d57",
"metadata": {},
"source": [
"Further customization ideas to explore: \n",
"\n",
"* Modify the layout such as by adding coordinates\n",
"* Use custom colors from [`palettable.wesanderson`](https://jiffyclub.github.io/palettable/) \n",
"* Change the size range for nodes"
]
},
{
"cell_type": "markdown",
"id": "311fbb2f",
"metadata": {},
"source": [
"## Saving the Visualization"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "efd0a82c",
"metadata": {},
"outputs": [],
"source": [
"# Use the save button in the rendered view. This produces a static image.\n",
"VG.render()"
]
},
{
"cell_type": "markdown",
"id": "a5128b85",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "11201815",
"metadata": {},
"outputs": [],
"source": [
"# Save the raw HTML output to a file. This allows to share the interactive visualization.\n",
"\n",
"import os\n",
"from neo4j_viz.options import Renderer\n",
"\n",
"os.makedirs(\"./out\", exist_ok=True)\n",
"\n",
"# Save the visualization to a file\n",
"with open(\"out/co_acted.html\", \"w\") as f:\n",
" f.write(VG.render(renderer=Renderer.CANVAS).data)"
]
},
{
"cell_type": "markdown",
"id": "71c3f41e",
"metadata": {},
"source": [
"## Cleanup"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "322d137f",
"metadata": {},
"outputs": [],
"source": [
"driver.close()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "429dec14",
"metadata": {},
"outputs": [],
"source": [
"gds.graph.get(\"demo-graph\").drop()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "010ab463",
"metadata": {},
"outputs": [],
"source": [
"gds.close()"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}