{ "cells": [ { "cell_type": "markdown", "id": "ae18b7ff", "metadata": {}, "source": [ "# Python: Basic usage of `r5py`\n", "\n", "```{admonition} Credits:\n", "\n", "This tutorial is a direct copy from [r5py -documentation](https://r5py.readthedocs.io/en/stable/notebooks/basic-usage.html) written by Henrikki Tenkanen, Christoph Fink & Willem Klumpenhouwer.\n", "\n", "```\n", "## Getting started\n", "\n", "### Run these codes in Binder\n", "\n", "Before you can run this Notebook, and/or do any programming, you need to launch the Binder instance. You can find buttons for activating the python environment at the top-right of this page which look like this:\n", "\n", "![Launch Binder](../img/launch_binder.png)\n", "\n", "### Working with Jupyter Notebooks\n", "\n", "Jupyter Notebooks are documents that can be used and run inside the JupyterLab programming environment containing the computer code and rich text elements (such as text, figures, tables and links). \n", "\n", "**A couple of hints**:\n", "\n", "- You can **execute a cell** by clicking a given cell that you want to run and pressing Shift + Enter (or by clicking the \"Play\" button on top)\n", "- You can **change the cell-type** between `Markdown` (for writing text) and `Code` (for writing/executing code) from the dropdown menu above. \n", "\n", "See **further details and help for** [**using Notebooks and JupyterLab from here**](https://pythongis.org/part1/chapter-01/nb/04-using-jupyterlab.html). \n", "\n", "\n", "## Introduction\n", "\n", "**R5py** is a Python library for routing and calculating travel time matrices on multimodal transport networks (walk, bike, public transport and car).\n", "It provides a simple and friendly interface to R5 (*the Rapid Realistic Routing on Real-world and Reimagined networks*) which is a [routing engine](https://github.com/conveyal/r5) developed by [Conveyal](https://conveyal.com/). `R5py` is designed to interact with [GeoPandas](https://geopandas.org) GeoDataFrames, and it is inspired by [r5r](https://ipeagit.github.io/r5r) which is a similar wrapper developed for R. `R5py` exposes some of R5’s functionality via its [Python API](reference.html), in a syntax similar to r5r’s. At the time of this writing, only the computation of travel time matrices has been fully implemented. Over time, `r5py` will be expanded to incorporate other functionalities from R5. " ] }, { "cell_type": "markdown", "id": "ca7f0d87", "metadata": {}, "source": [ "## Data requirements\n", "\n", "### Data for creating a routable network\n", "\n", "When calculating travel times with `r5py`, you typically need a couple of datasets: \n", "\n", "- **A road network dataset from OpenStreetMap** (OSM) in Protocolbuffer Binary (`.pbf`) -format: \n", " - This data is used for finding the fastest routes and calculating the travel times based on walking, cycling and driving. In addition, this data is used for walking/cycling legs between stops when routing with transit. \n", " - *Hint*: Sometimes you might need modify the OSM data beforehand, e.g. by cropping the data or adding special costs for travelling (e.g. for considering slope when cycling/walking). When doing this, you should follow the instructions at [Conveyal website](https://docs.conveyal.com/prepare-inputs#preparing-the-osm-data). For adding customized costs for pedestrian and cycling analyses, see [this repository](https://github.com/RSGInc/ladot_analysis_dataprep).\n", "\n", "- **A transit schedule dataset** in General Transit Feed Specification (GTFS.zip) -format (optional):\n", " - This data contains all the necessary information for calculating travel times based on public transport, such as stops, routes, trips and the schedules when the vehicles are passing a specific stop. You can read about [GTFS standard from here](https://developers.google.com/transit/gtfs/reference).\n", " - *Hint*: `r5py` can also combine multiple GTFS files, as sometimes you might have different GTFS feeds representing e.g. the bus and metro connections. \n", "\n", "\n", "### Data for origin and destination locations\n", "\n", "In addition to OSM and GTFS datasets, you need data that represents the origin and destination locations (OD-data) for routings. This data is typically stored in one of the geospatial data formats, such as Shapefile, GeoJSON or GeoPackage. As `r5py` is build on top of `geopandas`, it is easy to read OD-data from various different data formats. \n", "\n", "\n", "### Where to get these datasets?\n", "\n", "Here are a few places from where you can download the datasets for creating the routable network:\n", "\n", "- **OpenStreetMap data in PBF-format**:\n", "\n", " - [pyrosm](https://pyrosm.readthedocs.io/en/latest/basics.html#protobuf-file-what-is-it-and-how-to-get-one) -library. Allows downloading data directly from Python (based on GeoFabrik and BBBike).\n", " - [pydriosm](https://pydriosm.readthedocs.io/en/latest/quick-start.html#download-data) -library. Allows downloading data directly from Python (based on GeoFabrik and BBBike).\n", " - [GeoFabrik](http://download.geofabrik.de/) -website. Has data extracts for many pre-defined areas (countries, regions, etc).\n", " - [BBBike](https://download.bbbike.org/osm/bbbike/) -website. Has data extracts readily available for many cities across the world. Also supports downloading data by [specifying your own area or interest](https://extract.bbbike.org/).\n", " - [Protomaps](https://protomaps.com/downloads/osm) -website. Allows to download the data with custom extent by specifying your own area of interest.\n", "\n", "\n", "- **GTFS data**: \n", " - [Transitfeeds](https://transitfeeds.com/) -website. Easy to navigate and find GTFS data for different countries and cities. Includes current and historical GTFS data. Notice: The site will be depracated in the future. \n", " - [Mobility Database](https://database.mobilitydata.org) -website. Will eventually replace TransitFeeds -website. \n", " - [Transitland](https://www.transit.land/operators) -website. Find data based on country, operator or feed name. Includes current and historical GTFS data.\n", "\n", "### Sample datasets\n", "\n", "In the following tutorial, we use various open source datasets:\n", "- The point dataset for Helsinki has been obtained from [Helsinki Region Environmental Services](https://www.hsy.fi/en/environmental-information/open-data/avoin-data---sivut/population-grid-of-helsinki-metropolitan-area/) (HSY) licensed under a Creative Commons By Attribution 4.0. \n", "- The street network for Helsinki is a cropped and filtered extract of OpenStreetMap (© OpenStreetMap contributors, [ODbL license](https://www.openstreetmap.org/copyright))\n", "- The GTFS transport schedule dataset for Helsinki is a cropped and minimised copy of Helsingin seudun liikenne’s (HSL) open dataset [Creative Commons BY 4.0](https://www.hsl.fi/hsl/avoin-data#aineistojen-kayttoehdot)." ] }, { "cell_type": "markdown", "id": "f1b155a4", "metadata": {}, "source": [ "## Installation\n", "\n", "Before you can start using `r5py`, you need install it and a few libraries. Check [installation instructions](../installation.md) for more details. " ] }, { "cell_type": "markdown", "id": "4fcdf318", "metadata": {}, "source": [ "## Configuring `r5py` before using it\n", "\n", "It is possible to configure `r5py` in a few different ways (see [configuration instructions](../configuration.md) for details). One of the options that you most likely want to adjust, is **configuring how much memory** (RAM) `r5py` will consume during the calculations. `r5py` runs a powerful Java engine under the hood, and by default it will use **80 % of the available memory** for doing the calculations. However, you can easily adjust this. \n", "\n", "If you want to allocate e.g. a maximum of 5 Gb of RAM for the tool, you can do so by running:" ] }, { "cell_type": "code", "execution_count": 1, "id": "da226a30", "metadata": {}, "outputs": [], "source": [ "import sys\n", "sys.argv.append([\"--max-memory\", \"5G\"])" ] }, { "cell_type": "markdown", "id": "85ccfa2e", "metadata": {}, "source": [ "By running this, `r5py` will use **at maximum** 5 Gb of memory. However, it does not mean that the tool will necessary use all of this memory if it does not need it. \n", "\n", "```{important} \n", "Notice that changing the amount of allocated memory should alway be done as the first thing in your script, i.e. it should be run **before** importing `r5py`. \n", "```" ] }, { "cell_type": "markdown", "id": "09d69fcc", "metadata": {}, "source": [ "## Getting started with `r5py`\n", "\n", "Next, we will learn how to calculate travel times with `r5py` between locations spread around the city center area of Helsinki, Finland. \n", "\n", "### Load the origin and destination data\n", "\n", "Let's start by downloading a sample point dataset into a geopandas `GeoDataFrame` that we can use as our origin and destination locations. For the sake of this exercise, we have prepared a grid of points covering parts of Helsinki. The point data also contains information about residents of each 250 meter cell:" ] }, { "cell_type": "code", "execution_count": 2, "id": "b7267c21", "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", " \n", " \n", " \n", " \n", " \n", " \n", "
idpopulationgeometry
00389POINT (24.90770 60.16199)
11296POINT (24.90771 60.15974)
22636POINT (24.90772 60.15750)
331476POINT (24.90772 60.15526)
4423POINT (24.91219 60.16648)
\n", "
" ], "text/plain": [ " id population geometry\n", "0 0 389 POINT (24.90770 60.16199)\n", "1 1 296 POINT (24.90771 60.15974)\n", "2 2 636 POINT (24.90772 60.15750)\n", "3 3 1476 POINT (24.90772 60.15526)\n", "4 4 23 POINT (24.91219 60.16648)" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import geopandas \n", "\n", "points_url = \"https://github.com/r5py/r5py/raw/main/docs/data/Helsinki/population_points_2020.gpkg\"\n", "points = geopandas.read_file(points_url)\n", "points.head()" ] }, { "cell_type": "markdown", "id": "017b20af", "metadata": {}, "source": [ "The `points` GeoDataFrame contains a few columns, namely `id`, `population` and `geometry`. The `id` column with unique values and `geometry` columns are required for `r5py` to work. If your input point dataset does not have an `id` column with unique values, `r5py` will throw an error. \n", "\n", "To get a better sense of the data, let's create a map that shows the locations of the points and visualise the number of people living in each cell (the cells are represented by their centre point):" ] }, { "cell_type": "code", "execution_count": 3, "id": "ab441cc2", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "points.explore(\"population\", cmap=\"Reds\", marker_kwds={\"radius\": 12})" ] }, { "cell_type": "markdown", "id": "16b8142a", "metadata": {}, "source": [ "Let's pick one of these points to represent our **origin** and store it in a separate GeoDataFrame:" ] }, { "cell_type": "code", "execution_count": 4, "id": "d77b0649", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "origin = points.loc[points[\"id\"] == 54].copy()\n", "origin.explore(color=\"blue\", max_zoom=14, marker_kwds={\"radius\": 12})" ] }, { "cell_type": "markdown", "id": "7199be2c", "metadata": { "tags": [] }, "source": [ "### Load transport network\n", "\n", "Virtually all operations of `r5py` require a transport network. In this example, we use data from Helsinki metropolitan area, which you can find in the source code repository of r5py in `docs/data/` [(see here)](https://github.com/r5py/r5py/tree/main/docs/data). To import the street and public transport networks, instantiate an `r5py.TransportNetwork` with the file paths to the OSM extract and the GTFS files:" ] }, { "cell_type": "code", "execution_count": 5, "id": "07f29509", "metadata": { "nbsphinx": "hidden", "tags": [ "remove-cell" ] }, "outputs": [], "source": [ "# this cell is hidden from output\n", "# it’s used to set sys.path to point to the local repo\n", "import pathlib\n", "import sys\n", "sys.path.insert(0, str(pathlib.Path().absolute().parent.parent / \"src\"))" ] }, { "cell_type": "code", "execution_count": 6, "id": "35efb3fc", "metadata": {}, "outputs": [], "source": [ "# Allow 8 GB of memory\n", "import sys\n", "sys.argv.append([\"--max-memory\", \"8G\"])" ] }, { "cell_type": "code", "execution_count": 22, "id": "e2ac5e9f", "metadata": {}, "outputs": [], "source": [ "from r5py import TransportNetwork\n", "\n", "transport_network = TransportNetwork(\n", " \"demo_data/Helsinki/kantakaupunki.osm.pbf\",\n", " [\n", " \"demo_data/Helsinki/GTFS.zip\"\n", " ]\n", ")" ] }, { "cell_type": "markdown", "id": "1612fb47", "metadata": {}, "source": [ "At this stage, `r5py` has created the routable transport network and it is stored in the `transport_network` variable. We can now start using this network for doing the travel time calculations. " ] }, { "cell_type": "markdown", "id": "e91953f3", "metadata": {}, "source": [ "### Compute travel time matrix from one to all locations\n", "\n", "A travel time matrix is a dataset detailing the travel costs (e.g., time) between given locations (origins and destinations) in a study area. To compute a travel time matrix with `r5py` based on public transportation, we first need to initialize an `r5py.TravelTimeMatrixComputer` -object. As inputs, we pass following arguments for the `TravelTimeMatrixComputer`:\n", "- `transport_network`, which we created in the previous step representing the routable transport network. \n", "- `origins`, which is a GeoDataFrame with one location that we created earlier (however, you can also use multiple locations as origins).\n", "- `destinations`, which is a GeoDataFrame representing the destinations (in our case, the `points` GeoDataFrame). \n", "- `departure`, which should be Python's `datetime` -object (in our case standing for \"22nd of February 2022 at 08:30\") to tell `r5py` that the schedules of this specific time and day should be used for doing the calculations. \n", " - *Note*: By default, `r5py` summarizes and calculates a median travel time from all possible connections within one hour from given depature time (with 1 minute frequency). It is possible to adjust this time window using `departure_time_window` -parameter ([see details here]((https://r5py.readthedocs.io/en/stable/reference.html#r5py.RegionalTask))). \n", "- `transport_modes`, which determines the travel modes that will be used in the calculations. These can be passed using the options from the `TransitMode` and `LegMode` -classes. \n", " - *Hint*: To see all available options, run `help(TransitMode)` or `help(LegMode)`. \n", "\n", "```{note} In addition to these ones, the constructor also accepts many other parameters [listed here](https://r5py.readthedocs.io/en/stable/reference.html#r5py.RegionalTask), such as walking and cycling speed, maximum trip duration, maximum number of transit connections used during the trip, etc. \n", "```\n", "\n", "Now, we will first create a `travel_time_matrix_computer` instance as described above:" ] }, { "cell_type": "code", "execution_count": 9, "id": "279ab9fa", "metadata": {}, "outputs": [], "source": [ "import datetime\n", "from r5py import TravelTimeMatrixComputer, TransitMode, LegMode\n", "\n", "\n", "travel_time_matrix_computer = TravelTimeMatrixComputer(\n", " transport_network,\n", " origins=origin,\n", " destinations=points,\n", " departure=datetime.datetime(2022,2,22,8,30),\n", " transport_modes=[TransitMode.TRANSIT, LegMode.WALK]\n", ")\n" ] }, { "cell_type": "markdown", "id": "30e54135", "metadata": {}, "source": [ "Running this initializes the `TravelTimeMatrixComputer`, but any calculations were not done yet.\n", "To actually run the computations, we need to call `.compute_travel_times()` on the instance, which will calculate the travel times between all points:" ] }, { "cell_type": "code", "execution_count": 10, "id": "6dd5e975", "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", " \n", " \n", " \n", " \n", " \n", " \n", "
from_idto_idtravel_time
054026
154128
254228
354330
454428
\n", "
" ], "text/plain": [ " from_id to_id travel_time\n", "0 54 0 26\n", "1 54 1 28\n", "2 54 2 28\n", "3 54 3 30\n", "4 54 4 28" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "travel_time_matrix = travel_time_matrix_computer.compute_travel_times()\n", "travel_time_matrix.head()" ] }, { "cell_type": "markdown", "id": "7668d903", "metadata": {}, "source": [ "As a result, this returns a `pandas.DataFrame` which we stored in the `travel_time_matrix` -variable. The values in the `travel_time` column are travel times in minutes between the points identified by `from_id` and `to_id`. As you can see, the `id` value in the `from_id` column is the same for all rows because we only used one origin location as input. \n", "\n", "To get a better sense of the results, let's create a travel time map based on our results. We can do this easily by making a table join between the `points` GeoDataFrame and the `travel_time_matrix`. The key in the `travel_time_matrix` table is the column `to_id` and the corresponding key in `points` GeoDataFrame is the column `id`:" ] }, { "cell_type": "code", "execution_count": 11, "id": "51f5220e", "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idpopulationgeometryfrom_idto_idtravel_time
00389POINT (24.90770 60.16199)54026
11296POINT (24.90771 60.15974)54128
22636POINT (24.90772 60.15750)54228
331476POINT (24.90772 60.15526)54330
4423POINT (24.91219 60.16648)54428
\n", "
" ], "text/plain": [ " id population geometry from_id to_id travel_time\n", "0 0 389 POINT (24.90770 60.16199) 54 0 26\n", "1 1 296 POINT (24.90771 60.15974) 54 1 28\n", "2 2 636 POINT (24.90772 60.15750) 54 2 28\n", "3 3 1476 POINT (24.90772 60.15526) 54 3 30\n", "4 4 23 POINT (24.91219 60.16648) 54 4 28" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "join = points.merge(travel_time_matrix, left_on=\"id\", right_on=\"to_id\")\n", "join.head()" ] }, { "cell_type": "markdown", "id": "e51218a7", "metadata": {}, "source": [ "Now we have the travel times attached to each point, and we can easily visualize them on a map:" ] }, { "cell_type": "code", "execution_count": 12, "id": "a798eb4a", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "join.explore(\"travel_time\", cmap=\"Greens\", marker_kwds={\"radius\": 12})" ] }, { "cell_type": "markdown", "id": "f08f4f68", "metadata": {}, "source": [ "### Compute travel time matrix from all to all locations\n", "\n", "Running the calculations between all points in our sample dataset can be done in a similar manner as calculating the travel times from one origin to all destinations. \n", "Since, calculating these kind of all-to-all travel time matrices is quite typical when doing accessibility analyses, it is actually possible to calculate a cross-product between all points just by using the `origins` parameter (i.e. without needing to specify a separate set for destinations). `r5py` will use the same points as destinations and produce a full set of origins and destinations:\n" ] }, { "cell_type": "code", "execution_count": 13, "id": "00470da4", "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", " \n", " \n", " \n", " \n", " \n", " \n", "
from_idto_idtravel_time
0000
1017
20210
30318
40413
\n", "
" ], "text/plain": [ " from_id to_id travel_time\n", "0 0 0 0\n", "1 0 1 7\n", "2 0 2 10\n", "3 0 3 18\n", "4 0 4 13" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "travel_time_matrix_computer = TravelTimeMatrixComputer(\n", " transport_network,\n", " origins=points,\n", " departure=datetime.datetime(2022,2,22,8,30),\n", " transport_modes=[TransitMode.TRANSIT, LegMode.WALK]\n", ")\n", "travel_time_matrix_all = travel_time_matrix_computer.compute_travel_times()\n", "travel_time_matrix_all.head()" ] }, { "cell_type": "code", "execution_count": 14, "id": "8661a2a7", "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", " \n", " \n", " \n", " \n", " \n", " \n", "
from_idto_idtravel_time
87918727
88918823
89918910
9091906
9191910
\n", "
" ], "text/plain": [ " from_id to_id travel_time\n", "87 91 87 27\n", "88 91 88 23\n", "89 91 89 10\n", "90 91 90 6\n", "91 91 91 0" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "travel_time_matrix_all.tail()" ] }, { "cell_type": "code", "execution_count": 15, "id": "0abddcb7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8464" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(travel_time_matrix_all)" ] }, { "cell_type": "markdown", "id": "9cc8d6d4", "metadata": {}, "source": [ "As we can see from the outputs above, now we have calculated travel times between all points (n=92) in the study area. Hence, the resulting DataFrame has almost 8500 rows (92x92=8464). Based on these results, we can for example calculate the median travel time to or from a certain point, which gives a good estimate of the overall accessibility of the location in relation to other points:" ] }, { "cell_type": "code", "execution_count": 16, "id": "ae1eef0d", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "from_id\n", "0 22.0\n", "1 25.5\n", "2 29.0\n", "3 28.0\n", "4 26.0\n", " ... \n", "87 25.5\n", "88 24.0\n", "89 24.0\n", "90 25.5\n", "91 27.0\n", "Name: travel_time, Length: 92, dtype: float64" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "median_times = travel_time_matrix_all.groupby(\"from_id\")[\"travel_time\"].median()\n", "median_times" ] }, { "cell_type": "markdown", "id": "a8e421f2", "metadata": {}, "source": [ "To estimate, how long does it take in general to travel between locations in our study area (i.e. what is the baseline accessibility in the area), we can calculate the mean (or median) of the median travel times showing that it is approximately 22 minutes:" ] }, { "cell_type": "code", "execution_count": 17, "id": "a28b685d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "22.130434782608695" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "median_times.mean()" ] }, { "cell_type": "markdown", "id": "ea04a0df", "metadata": {}, "source": [ "Naturally, we can also visualize these values on a map:" ] }, { "cell_type": "code", "execution_count": 18, "id": "5fc9eb6b", "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idpopulationgeometryfrom_idtravel_time
00389POINT (24.90770 60.16199)022.0
11296POINT (24.90771 60.15974)125.5
22636POINT (24.90772 60.15750)229.0
331476POINT (24.90772 60.15526)328.0
4423POINT (24.91219 60.16648)426.0
\n", "
" ], "text/plain": [ " id population geometry from_id travel_time\n", "0 0 389 POINT (24.90770 60.16199) 0 22.0\n", "1 1 296 POINT (24.90771 60.15974) 1 25.5\n", "2 2 636 POINT (24.90772 60.15750) 2 29.0\n", "3 3 1476 POINT (24.90772 60.15526) 3 28.0\n", "4 4 23 POINT (24.91219 60.16648) 4 26.0" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "overall_access = points.merge(median_times.reset_index(), left_on=\"id\", right_on=\"from_id\")\n", "overall_access.head()" ] }, { "cell_type": "code", "execution_count": 19, "id": "843a22e8", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "overall_access.explore(\"travel_time\", cmap=\"Blues\", scheme=\"natural_breaks\", k=4, marker_kwds={\"radius\": 12})" ] }, { "cell_type": "markdown", "id": "6f61d849", "metadata": {}, "source": [ "In out study area, there seems to be a bit poorer accessibility in the Southern areas and on the edges of the region (i.e. we wittness a classic edge-effect here). " ] }, { "cell_type": "markdown", "id": "57523a91", "metadata": {}, "source": [ "## Advanced usage\n", "\n", "### Compute travel times with a detailed breakdown of the routing results" ] }, { "cell_type": "markdown", "id": "9c11ae67", "metadata": {}, "source": [ "In case you are interested in more detailed routing results, it is possible to specify `breakdown=True` once initializing the `TravelTimeMatrixComputer` object. This will provide not only the same information as in the previous examples, but it also brings more detailed information about the routings. When breakdown is enabled, `r5py` produces information about the used routes for each origin-destination pair, as well as total time disaggregated by access, waiting, in-vehicle and transfer times:" ] }, { "cell_type": "code", "execution_count": 20, "id": "b3f5ac6f", "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", " \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", " \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", "
from_idto_idtravel_timeroutesboard_stopsalight_stopsride_timesaccess_timeegress_timetransfer_timewait_timestotal_timen_iterations
054026[1020, 2143A][1050106, 1040280][1040144, 1201229][4.0, 5.0]3.89.82.3[1.3, 1.5]27.81
154128[1020, 31M1][1050106, 1040602][1040144, 1201602][4.0, 2.0]3.812.42.0[2.3, 3.1]29.72
254229[1022, 1009][1050106, 1040405][1040155, 1203426][6.0, 4.0]3.85.12.1[1.2, 1.6]23.81
354330[1022, 1008][1050105, 1201430][1201133, 1203403][7.0, 4.0]7.68.90.4[2.3, 4.3]34.51
454428[1030][1050106][1040144][4.0]3.821.70.0[1.8]31.42
\n", "
" ], "text/plain": [ " from_id to_id travel_time routes board_stops \\\n", "0 54 0 26 [1020, 2143A] [1050106, 1040280] \n", "1 54 1 28 [1020, 31M1] [1050106, 1040602] \n", "2 54 2 29 [1022, 1009] [1050106, 1040405] \n", "3 54 3 30 [1022, 1008] [1050105, 1201430] \n", "4 54 4 28 [1030] [1050106] \n", "\n", " alight_stops ride_times access_time egress_time transfer_time \\\n", "0 [1040144, 1201229] [4.0, 5.0] 3.8 9.8 2.3 \n", "1 [1040144, 1201602] [4.0, 2.0] 3.8 12.4 2.0 \n", "2 [1040155, 1203426] [6.0, 4.0] 3.8 5.1 2.1 \n", "3 [1201133, 1203403] [7.0, 4.0] 7.6 8.9 0.4 \n", "4 [1040144] [4.0] 3.8 21.7 0.0 \n", "\n", " wait_times total_time n_iterations \n", "0 [1.3, 1.5] 27.8 1 \n", "1 [2.3, 3.1] 29.7 2 \n", "2 [1.2, 1.6] 23.8 1 \n", "3 [2.3, 4.3] 34.5 1 \n", "4 [1.8] 31.4 2 " ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "travel_time_matrix_computer = TravelTimeMatrixComputer(\n", " transport_network,\n", " origins=origin,\n", " destinations=points,\n", " departure=datetime.datetime(2022,2,22,8,30),\n", " transport_modes=[TransitMode.TRANSIT, LegMode.WALK],\n", " breakdown=True,\n", ")\n", "travel_time_matrix_detailed = travel_time_matrix_computer.compute_travel_times()\n", "travel_time_matrix_detailed.head()" ] }, { "cell_type": "markdown", "id": "d6cf1a37", "metadata": {}, "source": [ "As you can see, the result contains much more information than earlier, see the following table for explanations:\n", "\n", "| Column | Description | Data type |\n", "| ------------- | -------------------------------------------------------------------- | --------- |\n", "| **routes** | The route-ids (lines) used during the trip | list |\n", "| **board_stops** | The stop-ids of the boarding stops | list |\n", "| **alight_stops** | The stop-ids of the alighting stops | list |\n", "| **ride_times** | In vehicle ride times of individual journey legs | list |\n", "| **access_time** | The time it takes for the \"first mile\" of a trip | float |\n", "| **egress_time** | The time it takes for the \"last mile\" of a trip | float |\n", "| **transfer_time** | The time it takes to transfer from vechile to another | float |\n", "| **wait_times** | The time(s) it take to wait for the vehicle at a stop | list |\n", "| **total_time** | Sum(ride_times, access_time, egress_time, transfer_time, wait_times) | float |\n", "| **n_iterations** | Number of iterations used for calculating the travel times | int |\n" ] }, { "cell_type": "markdown", "id": "3fffda1c", "metadata": {}, "source": [ "### Compute travel times for different percentiles\n", "\n", "Because `r5py` calculates travel times for all possible transit departure possibilities within an hour (with one minute frequency), we basically get a distribution of travel times. It is possible to gather and return information about the travel times at different percentiles of this distribution based on all computed trips (sorted from the fastest to slowest connections). By default, the returned time in `r5py` is the median travel time (i.e. `50`). You can access these percentiles by using a parameter `percentiles` which accepts a list of integers representing different percentiles, such as `[25, 50, 75]` which returns the travel times at those percentiles:" ] }, { "cell_type": "code", "execution_count": 21, "id": "8d4e1e59", "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
from_idto_idtravel_time_p25travel_time_p50travel_time_p75
0540232527
1541252730
2542262830
3543273032
4544252729
\n", "
" ], "text/plain": [ " from_id to_id travel_time_p25 travel_time_p50 travel_time_p75\n", "0 54 0 23 25 27\n", "1 54 1 25 27 30\n", "2 54 2 26 28 30\n", "3 54 3 27 30 32\n", "4 54 4 25 27 29" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "travel_time_matrix_computer = TravelTimeMatrixComputer(\n", " transport_network,\n", " origins=origin,\n", " destinations=points,\n", " departure=datetime.datetime(2022,2,22,8,30),\n", " transport_modes=[TransitMode.TRANSIT, LegMode.WALK],\n", " percentiles=[25, 50, 75],\n", ")\n", "travel_time_matrix_detailed = travel_time_matrix_computer.compute_travel_times()\n", "travel_time_matrix_detailed.head()" ] } ], "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.10.8" } }, "nbformat": 4, "nbformat_minor": 5 }