{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Visualisierung eines Netzwerks\n",
    "\n",
    "In diesem Beispiel wird ein Graph mit networkx erstellt und anschließend mit pyvis visualisiert. Der Graph basiert auf Beispieldaten. Es werden erste Optionen in den Bereichen Größe, Farbe und Form der Knoten und Mouse-Over-Texte gezeigt.\n",
    "\n",
    "Der Code basiert auf den Dokumentationen der beiden Bibliotheken:\n",
    "- [Networkx Dokumentation](https://networkx.org/documentation/stable/)\n",
    "- [Pyvis Dokumentation](https://pyvis.readthedocs.io/en/latest/index.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Installation der Bibliotheken\n",
    "\n",
    "Networkx ist eine Python Bibliothek zur Erstellung und Analyse von Netzwerken. Pyvis ist eine Python Bibliothek zur interaktiven Visualisierung von Netzwerkgraphen. Beide können mit `pip` installiert werden. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: networkx in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (2.6.3)\n",
      "Requirement already satisfied: pyvis in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (0.3.2)\n",
      "Requirement already satisfied: jinja2>=2.9.6 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from pyvis) (2.11.3)\n",
      "Requirement already satisfied: networkx>=1.11 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from pyvis) (2.6.3)\n",
      "Requirement already satisfied: ipython>=5.3.0 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from pyvis) (7.29.0)\n",
      "Requirement already satisfied: jsonpickle>=1.4.1 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from pyvis) (3.0.1)\n",
      "Requirement already satisfied: backcall in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (0.2.0)\n",
      "Requirement already satisfied: pexpect>4.3 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (4.8.0)\n",
      "Requirement already satisfied: jedi>=0.16 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (0.18.0)\n",
      "Requirement already satisfied: traitlets>=4.2 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (5.1.0)\n",
      "Requirement already satisfied: decorator in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (5.1.0)\n",
      "Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (3.0.20)\n",
      "Requirement already satisfied: matplotlib-inline in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (0.1.2)\n",
      "Requirement already satisfied: appnope in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (0.1.2)\n",
      "Requirement already satisfied: pygments in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (2.10.0)\n",
      "Requirement already satisfied: pickleshare in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (0.7.5)\n",
      "Requirement already satisfied: setuptools>=18.5 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from ipython>=5.3.0->pyvis) (58.0.4)\n",
      "Requirement already satisfied: parso<0.9.0,>=0.8.0 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from jedi>=0.16->ipython>=5.3.0->pyvis) (0.8.2)\n",
      "Requirement already satisfied: MarkupSafe>=0.23 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from jinja2>=2.9.6->pyvis) (1.1.1)\n",
      "Requirement already satisfied: ptyprocess>=0.5 in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from pexpect>4.3->ipython>=5.3.0->pyvis) (0.7.0)\n",
      "Requirement already satisfied: wcwidth in /Users/kim/opt/anaconda3/lib/python3.9/site-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython>=5.3.0->pyvis) (0.2.5)\n"
     ]
    }
   ],
   "source": [
    "# install networkx and pyvis using pip\n",
    "!pip install networkx\n",
    "!pip install pyvis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Erstellen eines Netzwerks mit Networkx\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import networkx as nx\n",
    "\n",
    "# create graph and use MultiGraph for nodes with multiple edges\n",
    "G = nx.MultiGraph()\n",
    "\n",
    "# create list of nodes with attributes as a dictionary\n",
    "nodes = [\n",
    "    (1, {\"label\": \"Firma 1\", \"branche\": \"Branche 1\", \"land\": \"Land 1\"}),\n",
    "    (2, {\"label\": \"Firma 2\", \"branche\": \"Branche 1\", \"land\": \"Land 2\"}),\n",
    "    (3, {\"label\": \"Firma 3\", \"branche\": \"Branche 1\", \"land\": \"Land 3\"}),\n",
    "    (4, {\"label\": \"Firma 4\", \"branche\": \"Branche 2\", \"land\": \"Land 4\"}),\n",
    "    (5, {\"label\": \"Firma 5\", \"branche\": \"Branche 2\", \"land\": \"Land 1\"}),\n",
    "    (6, {\"label\": \"Firma 6\", \"branche\": \"Branche 2\", \"land\": \"Land 3\"}),\n",
    "    (7, {\"label\": \"Firma 7\", \"branche\": \"Branche 3\", \"land\": \"Land 3\"}),\n",
    "    (8, {\"label\": \"Firma 8\", \"branche\": \"Branche 3\", \"land\": \"Land 2\"}),\n",
    "    (9, {\"label\": \"Firma 9\", \"branche\": \"Branche 4\", \"land\": \"Land 1\"}),\n",
    "    (10, {\"label\": \"Firma 10\", \"branche\": \"Branche 4\", \"land\": \"Land 4\"}),\n",
    "]\n",
    "\n",
    "# create list of edges with attributes as a dictionary\n",
    "edges = [\n",
    "    (1, 2, {\"label\": \"beziehung1\"}),\n",
    "    (5, 2, {\"label\": \"beziehung2\"}),\n",
    "    (1, 3, {\"label\": \"beziehung3\"}),\n",
    "    (2, 4, {\"label\": \"beziehung3\"}),\n",
    "    (2, 6, {\"label\": \"beziehung4\"}),\n",
    "    (2, 5, {\"label\": \"beziehung4\"}),\n",
    "    (8, 10, {\"label\": \"beziehung4\"}),\n",
    "    (9, 10, {\"label\": \"beziehung3\"}),\n",
    "    (3, 7, {\"label\": \"beziehung2\"}),\n",
    "    (6, 8, {\"label\": \"beziehung1\"}),\n",
    "    (6, 9, {\"label\": \"beziehung1\"}),\n",
    "    (1, 6, {\"label\": \"beziehung2\"}),\n",
    "]\n",
    "\n",
    "# add nodes to the graph\n",
    "G.add_nodes_from(nodes)\n",
    "\n",
    "# add edges to the graph, to hide arrow heads of the edges use option arrows = 'false'\n",
    "G.add_edges_from(edges, arrows=\"false\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Information für das Mouse-Over hinterlegen\n",
    "\n",
    "Anforderung: Wenn man mit der Maus über einzelne Knoten fährt, sollten weitere Informationen sichtbar werden\n",
    "\n",
    "Aktuelle Umsetzung: 'title' wird als String für jeden Knoten gesetzt aus Name der Firma und Anzahl der Verbindungen.\n",
    "\n",
    "Erweiterungen/offene Fragen: Weitere Stammdaten-Informationen sind möglich, sofern sie zur Verfügung stehen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "for node in G.nodes:\n",
    "    G.nodes[node][\"title\"] = (\n",
    "        G.nodes[node][\"label\"] + \"\\n\" + \"Anzahl Verbindungen: \" + str(G.degree[node])\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ändern der Größe der Knoten\n",
    "\n",
    "Anforderung: Größe in Abhängigkeit bestimmter Attribute ändern.\n",
    "\n",
    "Aktuelle Umsetzung: Setzen der Größe anhand der Anzahl der Kanten.\n",
    "\n",
    "Erweiterungen/offene Fragen: Weitere Attribute wie EBIT, Umsatz sollten möglich sein. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Erster Test zum Bestimmen der Verbindungen und der Anzahl"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2, 3, 6]\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "# get all nodes connected to node 1\n",
    "print(list(G.adj[1]))\n",
    "\n",
    "# get number of nodes connected to node 1\n",
    "print(G.degree[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Skalieren der Größe "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# scaling the size of the nodes by 5*degree\n",
    "scale = 5\n",
    "\n",
    "# getting all nodes and their number of connections\n",
    "d = dict(G.degree)\n",
    "\n",
    "# updating dict\n",
    "d.update((x, scale * (y + 1)) for x, y in d.items())\n",
    "\n",
    "# setting size attribute according to created dictionary\n",
    "nx.set_node_attributes(G, d, \"size\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualisierung mit Pyvis\n",
    "\n",
    "Beim Anlegen des Netzwerks kann mit `neighborhood_highlight=True` bereits aktiviert werden, dass ein Klick auf einen Knoten benachbarte Knoten hervorhebt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyvis.network import Network\n",
    "\n",
    "# create network, 'directed = true' allows multiple edges between nodes\n",
    "nt = Network(\n",
    "    \"1000px\",\n",
    "    \"1000px\",\n",
    "    neighborhood_highlight=True,\n",
    "    notebook=True,\n",
    "    cdn_resources=\"in_line\",\n",
    "    directed=True,\n",
    ")\n",
    "\n",
    "# populates the nodes and edges data structures\n",
    "nt.from_nx(G)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Erste Tests zum Ändern der Art und Farbe eines einzelnen Knotens\n",
    "\n",
    "Change shape of one node:\n",
    "`nt.nodes[1]['shape'] = 'square'`\n",
    "\n",
    "Change color of one node:\n",
    "`nt.nodes[1]['color'] = 'red'`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ändern der Farbe aller Knoten \n",
    "\n",
    "Anforderung: Ändere die Farbe basierend auf den Attributen \"Branche\" oder \"Land\"\n",
    "\n",
    "Aktuelle Umsetzung: Funktion, die die Farbe der Knoten anhand des ausgewählten Attributs (type) setzt.\n",
    "\n",
    "Erweiterungen/offene Fragen: Mögliche Branchen und Länder haben eine festcodierte Farbe, geht das generischer? Wie können weitere Attribute integriert werden?\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# define new function that sets the color of the nodes\n",
    "def color_type(net, type):\n",
    "    \"\"\"color_type sets the color of a network depending on an attribute of the nodes\n",
    "    net: network\n",
    "    type: 'branche' or 'land'\"\"\"\n",
    "\n",
    "    colormap = {\n",
    "        \"Branche 1\": \"#87CEEB\",\n",
    "        \"Branche 2\": \"#0f4c81\",\n",
    "        \"Branche 3\": \"#B2FFFF\",\n",
    "        \"Branche 4\": \"#191970\",\n",
    "        \"Land 1\": \"#F8D568\",\n",
    "        \"Land 2\": \"#F58025\",\n",
    "        \"Land 3\": \"#CC5500\",\n",
    "        \"Land 4\": \"#C0362C\",\n",
    "    }\n",
    "    for node in net.nodes:\n",
    "        node[\"color\"] = colormap[node[type]]\n",
    "    return net\n",
    "\n",
    "\n",
    "# set color based on attribute\n",
    "nt = color_type(nt, \"branche\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ändern der Farbe aller Kanten\n",
    "Normalerweise übernehmen die Kanten die Farben von ihren Knoten. Mit der Option 'color' kann für alle Kanten die gleiche Farbe gesetzt werden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set all edge colors\n",
    "nt.options.edges.color = \"grey\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Speichern des Netzwerks als HTML\n",
    "\n",
    "Die Ausrichtung und Spannkräfte des Netzwerks können mit den 'physics options' gesetzt werden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Netzwerk_Verflechtungsanalyse.html\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"1000px\"\n",
       "            height=\"1000px\"\n",
       "            src=\"Netzwerk_Verflechtungsanalyse.html\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x10b82b940>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# activate physics options to try out different solver\n",
    "# nt.show_buttons(filter_=['physics'])\n",
    "\n",
    "# set physics options\n",
    "nt.barnes_hut(\n",
    "    gravity=-8000,\n",
    "    central_gravity=0.3,\n",
    "    spring_length=200,\n",
    "    spring_strength=0.1,\n",
    "    damping=0.09,\n",
    "    overlap=0,\n",
    ")\n",
    "\n",
    "# create html and save in same folder\n",
    "nt.show(\"Netzwerk_Verflechtungsanalyse.html\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Erstellen eines minimalen Netzwerks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Netzwerk.html\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"1000px\"\n",
       "            height=\"1000px\"\n",
       "            src=\"Netzwerk.html\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x10bedbf70>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import networkx as nx\n",
    "from pyvis.network import Network\n",
    "\n",
    "sn = nx.Graph()\n",
    "sn_nodes = [1, 2, 3, 4, 5, 6, 7]\n",
    "sn_edges = [(1, 4), (2, 4), (3, 4), (4, 5), (5, 6), (5, 7)]\n",
    "\n",
    "sn.add_nodes_from(sn_nodes, color=\"#00509b\")\n",
    "sn.add_edges_from(sn_edges)\n",
    "\n",
    "net = Network(\"1000px\", \"1000px\", notebook=True, cdn_resources=\"in_line\")\n",
    "\n",
    "net.from_nx(sn)\n",
    "net.show(\"Netzwerk.html\")"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49"
  },
  "kernelspec": {
   "display_name": "Python 3.10.1 64-bit",
   "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.10.1"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
