{
"cells": [
{
"cell_type": "markdown",
"id": "29b63028-e476-4840-bfa8-933ab4a392a8",
"metadata": {
"tags": []
},
"source": [
"# Adding Color to Nodes"
]
},
{
"cell_type": "markdown",
"id": "f6767d51-ca29-4892-85a1-e4e4eaba4b4b",
"metadata": {},
"source": [
"Graphs where every node is the same color can be difficult to interpret. For this reason, we may want to represent each node as a distinct color given some trait of that node. In the example below, we will use a simple list of Harry Potter characters. We will use their houses to denote node color. Let's import the same libraries we have worked with so far in this chapter and throughout this textbook."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "bdd0cd67-4b47-4bb0-ac64-f2cc88f73572",
"metadata": {},
"outputs": [],
"source": [
"import networkx as nx\n",
"from pyvis.network import Network\n",
"import pandas as pd\n",
"from IPython.display import HTML"
]
},
{
"cell_type": "markdown",
"id": "fb4dc46b-c56e-440a-86a7-38a8a6112056",
"metadata": {},
"source": [
"Our data is stored as a csv file which we can load with Pandas."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "53282cf2-fc29-4d89-b5f6-1cc0df33a95b",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
name
\n",
"
house
\n",
"
color
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
Harry
\n",
"
Gryffindor
\n",
"
red
\n",
"
\n",
"
\n",
"
1
\n",
"
Hermione
\n",
"
Gryffindor
\n",
"
red
\n",
"
\n",
"
\n",
"
2
\n",
"
Ron
\n",
"
Gryffindor
\n",
"
red
\n",
"
\n",
"
\n",
"
3
\n",
"
Drako
\n",
"
Slytherine
\n",
"
green
\n",
"
\n",
"
\n",
"
4
\n",
"
Snape
\n",
"
Slytherine
\n",
"
green
\n",
"
\n",
"
\n",
"
5
\n",
"
Sedric
\n",
"
Hufflepuff
\n",
"
yellow
\n",
"
\n",
"
\n",
"
6
\n",
"
Luna
\n",
"
Ravenclaw
\n",
"
blue
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" name house color\n",
"0 Harry Gryffindor red\n",
"1 Hermione Gryffindor red\n",
"2 Ron Gryffindor red\n",
"3 Drako Slytherine green\n",
"4 Snape Slytherine green\n",
"5 Sedric Hufflepuff yellow\n",
"6 Luna Ravenclaw blue"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"node_df = pd.read_csv(\"../data/hp - nodes.csv\")\n",
"node_df"
]
},
{
"cell_type": "markdown",
"id": "cd9d2485-b2a8-41d5-8dbd-55b771816a22",
"metadata": {},
"source": [
"Now that we have our data loaded, we can iterate over each Pandas row and populate a list of nodes. Notice, that when we append to `nodes`, we are not appending a string, rather a tuple. Index 0 of the tuple is the character name, while index 1 is a dictionary. This is where we can store special attributes associated with the node. Think of this as metadata. In our case, we want two pieces of metadata, `color` and `house`."
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "dfdd6048-0936-45f4-9a8f-82984900e67d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[('Harry', {'color': 'red', 'house': 'Gryffindor'})]"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nodes = []\n",
"for idx, row in node_df.iterrows():\n",
" nodes.append((row[\"name\"], {\"color\": row.color, \"house\": row.house}))\n",
"nodes[:1]"
]
},
{
"cell_type": "markdown",
"id": "43b6e549-b933-47b6-b008-6719c56b411f",
"metadata": {},
"source": [
"Now that we have our node list created, let's populate it into a NetworkX `Graph`."
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "c04c2fe5-ec4f-40a7-8879-3d4dee925693",
"metadata": {},
"outputs": [],
"source": [
"G = nx.Graph()\n",
"G.add_nodes_from(nodes)"
]
},
{
"cell_type": "markdown",
"id": "49f2402e-430f-44fe-88d1-e6de752a472b",
"metadata": {},
"source": [
"From here, we can do the same thing with our edge list, which is also stored in a Pandas dataframe. NetworkX has built-in ways of creating a graph from a Pandas edge list, but we will do this manually here."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "c32f36b1-7fdf-4637-ad0a-08e513a38334",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"
\n",
"
source
\n",
"
target
\n",
"
\n",
" \n",
" \n",
"
\n",
"
0
\n",
"
Harry
\n",
"
Hermione
\n",
"
\n",
"
\n",
"
1
\n",
"
Hermione
\n",
"
Ron
\n",
"
\n",
"
\n",
"
2
\n",
"
Ron
\n",
"
Harry
\n",
"
\n",
"
\n",
"
3
\n",
"
Drako
\n",
"
Harry
\n",
"
\n",
"
\n",
"
4
\n",
"
Snape
\n",
"
Sedric
\n",
"
\n",
"
\n",
"
5
\n",
"
Sedric
\n",
"
Harry
\n",
"
\n",
"
\n",
"
6
\n",
"
Luna
\n",
"
Harry
\n",
"
\n",
"
\n",
"
7
\n",
"
Snape
\n",
"
Harry
\n",
"
\n",
"
\n",
"
8
\n",
"
Luna
\n",
"
Hermione
\n",
"
\n",
"
\n",
"
9
\n",
"
Ron
\n",
"
Drako
\n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" source target\n",
"0 Harry Hermione\n",
"1 Hermione Ron\n",
"2 Ron Harry\n",
"3 Drako Harry\n",
"4 Snape Sedric\n",
"5 Sedric Harry\n",
"6 Luna Harry\n",
"7 Snape Harry\n",
"8 Luna Hermione\n",
"9 Ron Drako"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"rel_df = pd.read_csv(\"../data/hp - rels.csv\")\n",
"rel_df"
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "5e61804a-66a8-44ca-95da-94ee5ce373a9",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[('Harry', 'Hermione')]"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"edge_list = []\n",
"for idx, row in rel_df.iterrows():\n",
" edge_list.append((row.source, row.target))\n",
"edge_list[:1]"
]
},
{
"cell_type": "markdown",
"id": "2a284252-5408-4f84-b463-0faebbf30a25",
"metadata": {},
"source": [
"Now that we have our edge list, we can inject it into the same `Graph` object."
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "9cba9c09-2e83-48b2-af44-4f4e07fdddfb",
"metadata": {},
"outputs": [],
"source": [
"G.add_edges_from(edge_list)"
]
},
{
"cell_type": "markdown",
"id": "ae10b9de-6cf0-47c8-9693-fea1281bce03",
"metadata": {},
"source": [
"At this stage, our NetworkX `Graph` is complete with all the data. We can now repeat the steps from the previous section and create our PyVis visualization from the data."
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "8ba45046-7414-4635-98a0-57bfe14ff1f4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Local cdn resources have problems on chrome/safari when used in jupyter-notebook. \n"
]
}
],
"source": [
"net = Network(notebook=True)"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "226dfcc0-e9e2-4504-8a4c-4c095e3a4669",
"metadata": {},
"outputs": [],
"source": [
"net.from_nx(G)"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "be2d18b7-1a03-43e3-9380-4d6fc6e009e0",
"metadata": {},
"outputs": [],
"source": [
"net.save_graph(\"hp_network.html\")"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "5a20b5a0-fada-4627-bc03-f49b15908053",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
"
\n",
"\n",
"
\n",
"\n",
"\n",
" \n",
" \n",
"\n",
"\n",
"
\n",
" \n",
"
\n",
" \n",
" \n",
"\n",
"\n",
" \n",
"
\n",
" \n",
" \n",
" \n",
"
\n",
"\n",
" \n",
" \n",
"\n",
" \n",
" \n",
""
],
"text/plain": [
""
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"HTML(filename=\"hp_network.html\")"
]
},
{
"cell_type": "markdown",
"id": "2831da88-4809-4202-a9ec-ac4d4e5348aa",
"metadata": {},
"source": [
"We now have a graph with nodes represented with different colors. Notice that the source node controls the edge color by default. NetworkX and PyVis both allow us to control the edge color in the same way as it did with nodes, but instead of adding `color` as an attribute of the `node`, we could also assign this to the `edge` as an attribute."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e672775a-2906-4bb4-a492-03dd30207118",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.12"
},
"toc-autonumbering": false
},
"nbformat": 4,
"nbformat_minor": 5
}