{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n\n# Graph from arbitrary data\n\nIn this example, we demonstrate how to create an AequilibraE Graph from an arbitrary network.\n\nWe are using \n[Sioux Falls data](https://github.com/bstabler/TransportationNetworks/tree/master/SiouxFalls), \nfrom TNTP.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        ".. seealso::\n    Several functions, methods, classes and modules are used in this example:\n\n    * :func:`aequilibrae.paths.Graph`\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Imports\nimport numpy as np\nimport pandas as pd\n\nfrom aequilibrae.paths import Graph"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We start by adding the path to load our arbitrary network.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "net_file = \"https://raw.githubusercontent.com/bstabler/TransportationNetworks/master/SiouxFalls/SiouxFalls_net.tntp\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's read our data! We'll be using Sioux Falls transportation network data, but without\ngeometric information. The data will be stored in a Pandas DataFrame containing information\nabout initial and final nodes, link distances, travel times, etc.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "net = pd.read_csv(net_file, skiprows=8, sep=\"\\t\", lineterminator=\"\\n\", usecols=np.arange(1, 11))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The Graph object requires several default fields: link_id, a_node, b_node, and direction.\n\nWe need to manipulate the data to add the missing fields (link_id and direction) and\nrename the node columns accordingly.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "net.insert(0, \"link_id\", np.arange(1, net.shape[0] + 1))\nnet = net.assign(direction=1)\nnet.rename(columns={\"init_node\": \"a_node\", \"term_node\": \"b_node\"}, inplace=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now we can take a look in our network file\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "net.head()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Building an AequilibraE graph from our network is pretty straightforward. We assign\nour network to be the graph's network ...\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "graph = Graph()\ngraph.network = net"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "... and then set the graph's configurations.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "graph.prepare_graph(np.arange(1, 25))  # sets the centroids for which we will perform computation\n\ngraph.set_graph(\"length\")  # sets the cost field for path computation\n\ngraph.set_skimming([\"length\", \"free_flow_time\"]) # sets the skims to be computed\n\ngraph.set_blocked_centroid_flows(False)  # we don't block flows through centroids because all nodes\n                                         # in the Sioux Falls network are centroids"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Two of AequilibraE's new features consist in directly computing path or skims.\n\nLet's compute the path between nodes 1 and 17...\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "res = graph.compute_path(1, 17)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "... and print the corresponding nodes...\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "res.path_nodes"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "... and the path links.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "res.path"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "For path computation, when we call the method ``graph.compute_path(1, 17)``, we are calling the class\n``PathComputation`` and storing its results into a variable.\n\nNotice that other methods related to path computation, such as ``milepost`` can also be used with\n``res``.\n\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "For skim computation, the process is quite similar. When calligng the method ``graph.compute_skims()``\nwe are actually calling the class ``NetworkSkimming``, and storing its results into ``skm``.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "skm = graph.compute_skims()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's get the values for 'free_flow_time' matrix.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "skims = skm.results.skims\nskims.get_matrix(\"free_flow_time\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now we're all set!\n\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Graph image credits to\n[Behance-network icons created by Sumitsaengtong - Flaticon](https://www.flaticon.com/free-icons/behance-network)\n"
      ]
    }
  ],
  "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.10.18"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}