Skip to content

Commit a3b17a6

Browse files
author
Mark Needham
committed
scc
1 parent 1467bd9 commit a3b17a6

File tree

3 files changed

+193
-108
lines changed

3 files changed

+193
-108
lines changed

generate_notebook.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ def find_tag(file, tag):
125125
session.write_transaction(lambda tx: tx.run(write_query))''' % write_query_content
126126

127127

128-
viz_intro_text = '''## Graph Visualisation
128+
viz_intro_text = '''\
129+
## Graph Visualisation
129130
130131
Sometimes a picture can tell more than a table of results and this is often the case with graph algorithms.
131132
Let's see how to create a graph visualization using neovis.js.

notebooks/StronglyConnectedComponents.ipynb

Lines changed: 176 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
},
1414
{
1515
"cell_type": "code",
16-
"execution_count": 1,
16+
"execution_count": null,
1717
"metadata": {},
1818
"outputs": [],
1919
"source": [
@@ -31,7 +31,7 @@
3131
},
3232
{
3333
"cell_type": "code",
34-
"execution_count": 2,
34+
"execution_count": null,
3535
"metadata": {},
3636
"outputs": [],
3737
"source": [
@@ -50,35 +50,28 @@
5050
},
5151
{
5252
"cell_type": "code",
53-
"execution_count": 3,
54-
"metadata": {},
55-
"outputs": [
56-
{
57-
"name": "stdout",
58-
"output_type": "stream",
59-
"text": [
60-
"Stats: {'labels-added': 6, 'relationships-created': 10, 'nodes-created': 6, 'properties-set': 6}\n"
61-
]
62-
}
63-
],
53+
"execution_count": null,
54+
"metadata": {},
55+
"outputs": [],
6456
"source": [
6557
"create_graph_query = '''\n",
66-
"CREATE (nAlice:User {id:'Alice'})\n",
67-
",(nBridget:User {id:'Bridget'})\n",
68-
",(nCharles:User {id:'Charles'})\n",
69-
",(nDoug:User {id:'Doug'})\n",
70-
",(nMark:User {id:'Mark'})\n",
71-
",(nMichael:User {id:'Michael'})\n",
72-
"CREATE (nAlice)-[:FOLLOW]->(nBridget)\n",
73-
",(nAlice)-[:FOLLOW]->(nCharles)\n",
74-
",(nMark)-[:FOLLOW]->(nDoug)\n",
75-
",(nMark)-[:FOLLOW]->(nMichael)\n",
76-
",(nBridget)-[:FOLLOW]->(nMichael)\n",
77-
",(nDoug)-[:FOLLOW]->(nMark)\n",
78-
",(nMichael)-[:FOLLOW]->(nAlice)\n",
79-
",(nAlice)-[:FOLLOW]->(nMichael)\n",
80-
",(nBridget)-[:FOLLOW]->(nAlice)\n",
81-
",(nMichael)-[:FOLLOW]->(nBridget);\n",
58+
"MERGE (nAlice:User {id:'Alice'})\n",
59+
"MERGE (nBridget:User {id:'Bridget'})\n",
60+
"MERGE (nCharles:User {id:'Charles'})\n",
61+
"MERGE (nDoug:User {id:'Doug'})\n",
62+
"MERGE (nMark:User {id:'Mark'})\n",
63+
"MERGE (nMichael:User {id:'Michael'})\n",
64+
"\n",
65+
"MERGE (nAlice)-[:FOLLOW]->(nBridget)\n",
66+
"MERGE (nAlice)-[:FOLLOW]->(nCharles)\n",
67+
"MERGE (nMark)-[:FOLLOW]->(nDoug)\n",
68+
"MERGE (nMark)-[:FOLLOW]->(nMichael)\n",
69+
"MERGE (nBridget)-[:FOLLOW]->(nMichael)\n",
70+
"MERGE (nDoug)-[:FOLLOW]->(nMark)\n",
71+
"MERGE (nMichael)-[:FOLLOW]->(nAlice)\n",
72+
"MERGE (nAlice)-[:FOLLOW]->(nMichael)\n",
73+
"MERGE (nBridget)-[:FOLLOW]->(nAlice)\n",
74+
"MERGE (nMichael)-[:FOLLOW]->(nBridget);\n",
8275
"'''\n",
8376
"\n",
8477
"with driver.session() as session:\n",
@@ -95,84 +88,9 @@
9588
},
9689
{
9790
"cell_type": "code",
98-
"execution_count": 4,
99-
"metadata": {},
100-
"outputs": [
101-
{
102-
"data": {
103-
"text/html": [
104-
"<div>\n",
105-
"<style scoped>\n",
106-
" .dataframe tbody tr th:only-of-type {\n",
107-
" vertical-align: middle;\n",
108-
" }\n",
109-
"\n",
110-
" .dataframe tbody tr th {\n",
111-
" vertical-align: top;\n",
112-
" }\n",
113-
"\n",
114-
" .dataframe thead th {\n",
115-
" text-align: right;\n",
116-
" }\n",
117-
"</style>\n",
118-
"<table border=\"1\" class=\"dataframe\">\n",
119-
" <thead>\n",
120-
" <tr style=\"text-align: right;\">\n",
121-
" <th></th>\n",
122-
" <th>name</th>\n",
123-
" <th>partition</th>\n",
124-
" </tr>\n",
125-
" </thead>\n",
126-
" <tbody>\n",
127-
" <tr>\n",
128-
" <th>0</th>\n",
129-
" <td>Mark</td>\n",
130-
" <td>0</td>\n",
131-
" </tr>\n",
132-
" <tr>\n",
133-
" <th>1</th>\n",
134-
" <td>Michael</td>\n",
135-
" <td>1</td>\n",
136-
" </tr>\n",
137-
" <tr>\n",
138-
" <th>2</th>\n",
139-
" <td>Charles</td>\n",
140-
" <td>2</td>\n",
141-
" </tr>\n",
142-
" <tr>\n",
143-
" <th>3</th>\n",
144-
" <td>Doug</td>\n",
145-
" <td>0</td>\n",
146-
" </tr>\n",
147-
" <tr>\n",
148-
" <th>4</th>\n",
149-
" <td>Alice</td>\n",
150-
" <td>1</td>\n",
151-
" </tr>\n",
152-
" <tr>\n",
153-
" <th>5</th>\n",
154-
" <td>Bridget</td>\n",
155-
" <td>1</td>\n",
156-
" </tr>\n",
157-
" </tbody>\n",
158-
"</table>\n",
159-
"</div>"
160-
],
161-
"text/plain": [
162-
" name partition\n",
163-
"0 Mark 0\n",
164-
"1 Michael 1\n",
165-
"2 Charles 2\n",
166-
"3 Doug 0\n",
167-
"4 Alice 1\n",
168-
"5 Bridget 1"
169-
]
170-
},
171-
"execution_count": 4,
172-
"metadata": {},
173-
"output_type": "execute_result"
174-
}
175-
],
91+
"execution_count": null,
92+
"metadata": {},
93+
"outputs": [],
17694
"source": [
17795
"streaming_query = \"\"\"\n",
17896
"CALL algo.scc.stream('User','FOLLOW')\n",
@@ -196,6 +114,157 @@
196114
"The first and biggest component has members Alice, Bridget, and Michael, while the second component has Doug and Mark.\n",
197115
"Charles ends up in his own component becuase there isn't an outgoing relationship from that node to any of the others."
198116
]
117+
},
118+
{
119+
"cell_type": "markdown",
120+
"metadata": {},
121+
"source": [
122+
"We can also call a version of the algorithm that will store the result as a property on a\n",
123+
"node. This is useful if we want to run future queries that use the result."
124+
]
125+
},
126+
{
127+
"cell_type": "code",
128+
"execution_count": null,
129+
"metadata": {},
130+
"outputs": [],
131+
"source": [
132+
"write_query = \"\"\"\n",
133+
"CALL algo.scc('User','FOLLOW', {write:true,partitionProperty:'partition'})\n",
134+
"YIELD loadMillis, computeMillis, writeMillis, setCount, maxSetSize, minSetSize;\n",
135+
"\"\"\"\n",
136+
"\n",
137+
"with driver.session() as session:\n",
138+
" session.write_transaction(lambda tx: tx.run(write_query))"
139+
]
140+
},
141+
{
142+
"cell_type": "markdown",
143+
"metadata": {},
144+
"source": [
145+
"## Graph Visualisation\n",
146+
"\n",
147+
"Sometimes a picture can tell more than a table of results and this is often the case with graph algorithms. \n",
148+
"Let's see how to create a graph visualization using neovis.js.\n",
149+
"\n",
150+
"First we'll create a div into which we will generate the visualisation."
151+
]
152+
},
153+
{
154+
"cell_type": "code",
155+
"execution_count": null,
156+
"metadata": {},
157+
"outputs": [],
158+
"source": [
159+
"%%html\n",
160+
"<style type=\"text/css\"> \n",
161+
".output_wrapper, .output {\n",
162+
" height:auto !important;\n",
163+
" max-height:600px;\n",
164+
"}\n",
165+
".output_scroll {\n",
166+
" box-shadow:none !important;\n",
167+
" webkit-box-shadow:none !important;\n",
168+
"}\n",
169+
"\n",
170+
"#viz {\n",
171+
" width: 300px;\n",
172+
" height: 350px;\n",
173+
" font: 22pt arial;\n",
174+
"}\n",
175+
"</style> \n",
176+
"<div id=\"viz\"></div>"
177+
]
178+
},
179+
{
180+
"cell_type": "markdown",
181+
"metadata": {},
182+
"source": [
183+
"Next we need to define the query that the visualization will be generated from, along with config \n",
184+
"that describes which properties will be used for node size, node colour, and relationship width. \n",
185+
"\n",
186+
"We'll then define a JavaScript variable that contains all our parameters."
187+
]
188+
},
189+
{
190+
"cell_type": "code",
191+
"execution_count": null,
192+
"metadata": {},
193+
"outputs": [],
194+
"source": [
195+
"from IPython.core.display import Javascript\n",
196+
"import json\n",
197+
"from scripts.algo import viz_config, render_image\n",
198+
"\n",
199+
"config = viz_config(\"Strongly Connected Components\")\n",
200+
"query = config[\"query\"]\n",
201+
"labels_json = config[\"labels_json\"]\n",
202+
"relationships_json = config[\"relationships_json\"]\n",
203+
"\n",
204+
"json_graph = {\n",
205+
" \"query\": query,\n",
206+
" \"labels\": labels_json,\n",
207+
" \"relationships\": relationships_json,\n",
208+
" \"host\": host,\n",
209+
" \"user\": user,\n",
210+
" \"password\": password\n",
211+
"}\n",
212+
"\n",
213+
"Javascript(\"\"\"window.jsonGraph={};\"\"\".format(json.dumps(json_graph)))"
214+
]
215+
},
216+
{
217+
"cell_type": "markdown",
218+
"metadata": {},
219+
"source": [
220+
"Now we're ready to call neovis.js and generate our graph visualisation. \n",
221+
"The following code will create an interactive graph into the div defined above.\n",
222+
"It will also extract an image representation of the graph and display that in the cell below."
223+
]
224+
},
225+
{
226+
"cell_type": "code",
227+
"execution_count": null,
228+
"metadata": {},
229+
"outputs": [],
230+
"source": [
231+
"%%javascript\n",
232+
"var output_area = this;\n",
233+
"requirejs(['neovis.js'], function(NeoVis){ \n",
234+
" var config = {\n",
235+
" container_id: \"viz\",\n",
236+
" server_url: window.jsonGraph.host,\n",
237+
" server_user: window.jsonGraph.user,\n",
238+
" server_password: window.jsonGraph.password,\n",
239+
" labels: window.jsonGraph.labels,\n",
240+
" relationships: window.jsonGraph.relationships,\n",
241+
" initial_cypher: window.jsonGraph.query\n",
242+
" };\n",
243+
" \n",
244+
" let viz = new NeoVis.default(config);\n",
245+
" viz.render();\n",
246+
" \n",
247+
" viz.onVisualizationRendered(function(ctx) {\n",
248+
" let imageSrc = ctx.canvas.toDataURL();\n",
249+
" let kernel = IPython.notebook.kernel;\n",
250+
" let command = \"image_src = '\" + imageSrc + \"'\";\n",
251+
" kernel.execute(command);\n",
252+
" \n",
253+
" var cell_element = output_area.element.parents('.cell');\n",
254+
" var cell_idx = Jupyter.notebook.get_cell_elements().index(cell_element);\n",
255+
" var cell = Jupyter.notebook.get_cell(cell_idx+1);\n",
256+
" cell.set_text(\"render_image(image_src)\")\n",
257+
" cell.execute();\n",
258+
" });\n",
259+
"});"
260+
]
261+
},
262+
{
263+
"cell_type": "code",
264+
"execution_count": null,
265+
"metadata": {},
266+
"outputs": [],
267+
"source": []
199268
}
200269
],
201270
"metadata": {},

notebooks/scripts/algo.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ def viz_config(algorithm_name):
6363
'caption': False
6464
}
6565
}
66+
},
67+
"Strongly Connected Components": {
68+
"query": "MATCH (p1:User)-[r:FOLLOW]->(p2:User) RETURN *",
69+
"labels_json": {
70+
'User': {
71+
'caption': 'id',
72+
'community': 'partition'
73+
}
74+
},
75+
"relationships_json": {
76+
'FOLLOW': {
77+
'thickness': 'weight',
78+
'caption': False
79+
}
80+
}
6681
}
6782
}[algorithm_name]
6883

0 commit comments

Comments
 (0)