<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Distances | Lenka&#39;s blog</title>
    <link>https://lenkastatham.com/categories/distances.html</link>
      <atom:link href="https://lenkastatham.com/categories/distances/index.xml" rel="self" type="application/rss+xml" />
    <description>Distances</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-gb</language><lastBuildDate>Wed, 22 Jun 2022 00:00:00 +0000</lastBuildDate>
    <image>
      <url>https://lenkastatham.com/img/icon-192.png</url>
      <title>Distances</title>
      <link>https://lenkastatham.com/categories/distances.html</link>
    </image>
    
    <item>
      <title>Retrieving custom networks from OSM using Pyrosm and translating to Pandana and NetworkX</title>
      <link>https://lenkastatham.com/post/pyrosm.html</link>
      <pubDate>Wed, 22 Jun 2022 00:00:00 +0000</pubDate>
      <guid>https://lenkastatham.com/post/pyrosm.html</guid>
      <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://pyrosm.readthedocs.io/en/latest/&#34;&gt;Pyrosm&lt;/a&gt; is a very clever python library, written by &lt;a href=&#34;https://twitter.com/tenkahen?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor&#34;&gt;Henrikki Tenkane&lt;/a&gt;, that allows users to quickly and effectively retrieve data from OpenStreetMap, using Cython and Protocolbuffer Binary Format -files (PBFs). It is ultimately the most useful tool to retrieve OSM data within python and is fairly well documented.&lt;/p&gt;
&lt;p&gt;Yet, there were few places which can be confusing and where extended description and tutorial is needed. This is specifically;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;retrieving custom network (cycling example)&lt;/li&gt;
&lt;li&gt;converting custom network to Pandana/Networkx graph&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this tutorial, I&#39;ll also repeat the distances calculation with pandana from &lt;a href=&#34;https://lenkahas.com/post/pandana.html&#34;&gt;this post&lt;/a&gt; and update on any changes.&lt;/p&gt;
&lt;p&gt;I would have like to contribute to the Pyrosm library with the first point, however, Cython is not amongst my skills right now. So if someone knows how to do that, feel free.&lt;/p&gt;
&lt;h2 id=&#34;retrieving-custom-drivingwalkingcycling-network&#34;&gt;Retrieving custom driving/walking/cycling network&lt;/h2&gt;
&lt;p&gt;The Pyrosm uses &lt;a href=&#34;https://pyrosm.readthedocs.io/en/latest/custom_filter.html&#34;&gt;custom_filter&lt;/a&gt; to define what features should be extracted from the PBF, however, this filter is not available for the networks. Instead the Pyrosm offers &lt;a href=&#34;https://pyrosm.readthedocs.io/en/latest/custom_filter.html#keep-vs-exclude-data-with-custom-filters&#34;&gt;get_data_by_custom_criteria() and keep vs exclude feature&lt;/a&gt; which allows you to define which keys to keep or exclude from the OSM. This function also allows you to choose if you want to keep the nodes in the data, but those are not the network nodes, those are point features that has the tags defined in your custom filter. This means, that you can get custom network, but only the edge list. However, in order to construct graph in &lt;a href=&#34;https://udst.github.io/pandana/network.html&#34;&gt;Pandana&lt;/a&gt; we need both edge list and node list.
Now you could just get the edge list, create a graph in &lt;a href=&#34;https://networkx.org/documentation/stable/&#34;&gt;Networkx&lt;/a&gt; and then translate to Pandana by exporting edgelist and node list out, but let&#39;s be realistic. If there is a way to avoid multiple conversions and installing another package, I would be much happier.&lt;/p&gt;
&lt;p&gt;I found an easy way to retrieve both nodes and edges for custom created network. The process is as follows;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Retrieve complete network for the selected area&lt;/li&gt;
&lt;li&gt;Filter out the edges features based on selected keys from OSM&lt;/li&gt;
&lt;li&gt;List all the existing nodes in the edge dataframe&lt;/li&gt;
&lt;li&gt;Filter the nodes by the list from the previous step&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# import the necessary packages
import geopandas as gpd
import pandas as pd

# import pyrosm functions
from pyrosm import OSM
from pyrosm import get_data
# network libraries
import pandana
import networkx as nx
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def get_network_custom(place,filter_type):
    
    &amp;quot;&amp;quot;&amp;quot;
    place: string 
        name of the pbf to be downloaded 
        
    filter_type: string
        &#39;cycling&#39; only for now
    &amp;quot;&amp;quot;&amp;quot;
    # connect to the data
    osm = OSM(get_data(place))
    
    ## if you have already downloaded pbf file and it’s in your file directory use following
    ## osm = OSM(place)
    ## where place is &#39;filepath.pbf&#39;
    
    # retrieve the complete network 
    nodes,edges = osm.get_network(nodes=True, network_type=&amp;quot;all&amp;quot;)
    
    
    # define the filter
    if filter_type == &#39;cycling&#39;:
        highway = [&#39;primary&#39;,
                &#39;primary_link&#39;,
                &#39;trunk&#39;,
                &#39;trunk_link&#39;,
                &#39;secondary&#39;,
                &#39;secondary_link&#39;,
                &#39;tertiary&#39;,
                &#39;tertiary_link&#39;,
                &#39;unclassified&#39;,
                &#39;residential&#39;,
                &#39;living_street&#39;, 
                &#39;road&#39;,   
                &#39;service&#39;,
                &#39;track&#39;,
                &#39;path&#39;,
                &#39;pedestrian&#39;,
                &#39;footway&#39;,
                &#39;bridleway&#39;,
                &#39;cycleway&#39;,
                &#39;busway&#39;]
    else: raise ValueError(&#39;try cycling&#39;)
    # you could define other profiles such as driving or walking
       
    # choose only those edges that are inside the filter
    edges = edges[edges[&#39;highway&#39;].isin(highway)]
    
    # list origin nodes
    list_origins = list(edges.u.unique())
    
    # list destination nodes
    list_destinations = list(edges.v.unique())
    
    # combine the to lists to see all nodes that exist in filtered network 
    list_nodes = list_origins + list_destinations
    
    # cut the nodes by the existing list
    nodes_gdf = nodes[nodes.id.isin(list_nodes)] 
            
    return nodes_gdf,edges

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;nodes, edges = get_network_custom(&#39;Bristol&#39;,&#39;cycling&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;ax = edges.plot()
nodes.plot(ax = ax, color = &#39;orange&#39;, markersize = 2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./pyrosm/index_5_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;converting-custom-network-to-pandananetworkx-graph&#34;&gt;Converting custom network to pandana/networkx graph&lt;/h2&gt;
&lt;p&gt;Converting the two geodataframes to graphs is then dead easy.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;nodes.info()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;class &#39;geopandas.geodataframe.GeoDataFrame&#39;&amp;gt;
Int64Index: 515871 entries, 0 to 522320
Data columns (total 8 columns):
 #   Column     Non-Null Count   Dtype   
---  ------     --------------   -----   
 0   lon        515871 non-null  float64 
 1   lat        515871 non-null  float64 
 2   tags       20935 non-null   object  
 3   timestamp  515871 non-null  int8    
 4   version    515871 non-null  int8    
 5   changeset  515871 non-null  int8    
 6   id         515871 non-null  int64   
 7   geometry   515871 non-null  geometry
dtypes: float64(2), geometry(1), int64(1), int8(3), object(1)
memory usage: 25.1+ MB
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;edges.info()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;class &#39;geopandas.geodataframe.GeoDataFrame&#39;&amp;gt;
Int64Index: 551813 entries, 0 to 560619
Data columns (total 43 columns):
 #   Column          Non-Null Count   Dtype   
---  ------          --------------   -----   
 0   access          45542 non-null   object  
 1   area            10881 non-null   object  
 2   bicycle         40145 non-null   object  
 3   bridge          2960 non-null    object  
 4   busway          84 non-null      object  
 5   cycleway        7300 non-null    object  
 6   est_width       785 non-null     object  
 7   foot            87694 non-null   object  
 8   footway         5027 non-null    object  
 9   highway         551813 non-null  object  
 10  int_ref         0 non-null       object  
 11  junction        6390 non-null    object  
 12  lanes           30165 non-null   object  
 13  lit             49202 non-null   object  
 14  maxspeed        97150 non-null   object  
 15  motorcar        672 non-null     object  
 16  motorroad       8 non-null       object  
 17  motor_vehicle   8631 non-null    object  
 18  name            202888 non-null  object  
 19  oneway          49142 non-null   object  
 20  overtaking      61 non-null      object  
 21  path            835 non-null     object  
 22  passing_places  47 non-null      object  
 23  psv             873 non-null     object  
 24  ref             25646 non-null   object  
 25  service         45451 non-null   object  
 26  segregated      13048 non-null   object  
 27  sidewalk        38667 non-null   object  
 28  smoothness      2099 non-null    object  
 29  surface         145345 non-null  object  
 30  tracktype       5161 non-null    object  
 31  tunnel          1281 non-null    object  
 32  turn            7 non-null       object  
 33  width           3221 non-null    object  
 34  id              551813 non-null  int64   
 35  timestamp       551813 non-null  int64   
 36  version         551813 non-null  int8    
 37  tags            183067 non-null  object  
 38  osm_type        551813 non-null  object  
 39  geometry        551813 non-null  geometry
 40  u               551813 non-null  int64   
 41  v               551813 non-null  int64   
 42  length          551813 non-null  float64 
dtypes: float64(1), geometry(1), int64(4), int8(1), object(36)
memory usage: 181.6+ MB
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# This might be necessary if you get the error 
## ValueError: Buffer dtype mismatch, expected &#39;long&#39; but got &#39;double&#39;
nodes2 = nodes.set_index(&#39;id&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create pandana graph
pandana_graph = pandana.Network(nodes2[&#39;lon&#39;], 
                           nodes2[&#39;lat&#39;], 
                           edges[&#39;u&#39;], 
                           edges[&#39;v&#39;], 
                           edges[[&#39;length&#39;]],
                           twoway=[False,True])
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create networkx graph
networkx_graph = nx.from_pandas_edgelist(edges,source = &#39;u&#39;, target = &#39;v&#39;, edge_attr = &#39;length&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;calculating-shortest-distance-between-set-of-points-using-pandana&#34;&gt;Calculating shortest distance between set of points using pandana&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;pois = gpd.read_file(&amp;quot;./points.geojson&amp;quot;)
pois2 = pois.rename(columns = {&#39;ID_code&#39;:&#39;code_ID&#39;})
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import itertools
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def get_shortest_path(points, polygons, point_id, poly_id, Graph):
    # check the projections
    if points.crs != &#39;EPSG:4326&#39;:
        raise ValueError(&amp;quot;The point CRS is not Mercator, Use gpd.to_crs(&#39;EPSG4326&#39;) to transform the CRS&amp;quot;)

    if polygons.crs != &#39;EPSG:4326&#39;:
        raise ValueError(&amp;quot;The polygon CRS is not Mercator, Use gpd.to_crs(&#39;EPSG4326&#39;) to transform the CRS&amp;quot;)

        &amp;quot;&amp;quot;&amp;quot;
        Parses graph and two sets of locational datasets and returns dataframe with Origin destination ids, distances and times along the network.
        
        Parameters
        ----------
        point : point geodataframe 
        polygons : polygon or second point geodataframe
        point_id : str
            unique ID column of points
        poly_id : str
            unique ID column of polygons or the second points
         Graph: object 
            pandana.network
        
        Returns
        -------
        dataframe with distances and times between each pair of points
        
        Return type
        -----------
        pandas.dataframe
        &amp;quot;&amp;quot;&amp;quot;


    # define the unique origins and destinations
    origins = list(points[point_id].unique())
    destinations = list(polygons[poly_id].unique())
    # create OD data
    od_data = pd.DataFrame(list(itertools.product(origins,destinations))).rename(columns = {0:point_id,1:poly_id})
    # add coordinates to the data
    # if the data is other then points, it would take centrooid of the geometry
    
    if points.type[0] == &#39;Point&#39;:
        point_xy = pd.DataFrame({point_id: points[point_id], &#39;X&#39;:points.geometry.x, &#39;Y&#39;:points.geometry.y}).reset_index(drop=True)
    elif points.type[0] == &#39;MultiPoint&#39;:
        point_xy = pd.DataFrame({point_id: points[point_id], &#39;X&#39;:points.geometry.x, &#39;Y&#39;:points.geometry.y}).reset_index(drop=True)
    else: point_xy = pd.DataFrame({point_id: points[point_id], &#39;X&#39;:points.geometry.centroid.x, &#39;Y&#39;:points.geometry.centroid.y}).reset_index(drop=True)
    
    
    if polygons.type[0] == &#39;Point&#39;:
        poly_xy = pd.DataFrame({poly_id: polygons[poly_id], &#39;X&#39;:polygons.geometry.x, &#39;Y&#39;:polygons.geometry.y}).reset_index(drop=True)
    elif polygons.type[0] == &#39;MultiPoint&#39;:
        poly_xy = pd.DataFrame({poly_id: polygons[poly_id], &#39;X&#39;:polygons.geometry.x, &#39;Y&#39;:polygons.geometry.y}).reset_index(drop=True)
    else: poly_xy = pd.DataFrame({poly_id: polygons[poly_id], &#39;X&#39;:polygons.geometry.centroid.x, &#39;Y&#39;:polygons.geometry.centroid.y}).reset_index(drop=True)
    
    # build origin destination data    
    od_data = od_data.merge(point_xy, on=point_id, how = &#39;left&#39;).merge(poly_xy, on = poly_id, how = &#39;left&#39;)

    # find nodes on the network
    origin_nodes = Graph.get_node_ids(od_data.X_x, od_data.Y_x).values
    dests_nodes = Graph.get_node_ids(od_data.X_y, od_data.Y_y).values

    # find the distances and times
    od_data[&#39;distance&#39;] = pd.Series(Graph.shortest_path_lengths(origin_nodes, dests_nodes, &#39;length&#39;))
            
   
    return od_data
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import time

start = time.time()

dists = get_shortest_path(points = pois, polygons = pois2, point_id = &#39;ID_code&#39;, poly_id = &#39;code_ID&#39;, Graph = pandana_graph)

end = time.time()
print(end - start )
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;9.235082149505615
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;dists.distance.hist();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./pyrosm/index_17_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Notes: You can see that there is a suspicious distance that are bigger than 300 000 meters. This happens when the graph can&#39;t find the route for set of two points. This is most likely because one of the nodes is on disconnected graphs within the network and can&#39;t reach the other ones, or they could be outside the graph extent or something else. It would be smart to investigate the graph, but that’s not part of this post :)&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
    <item>
      <title>Calculating Walking distance in Python. Networkx vs Pandana.</title>
      <link>https://lenkastatham.com/post/pandana.html</link>
      <pubDate>Fri, 12 Mar 2021 00:00:00 +0000</pubDate>
      <guid>https://lenkastatham.com/post/pandana.html</guid>
      <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Let&#39;s say you have spatial point data and you want to calculate walking distances between all combination of the points. For example, you have data of local bars and you want to find out all the walking distances between them, so you can include them in your regression model (or anything else), for example as a variable that defines how accessible two pubs are to each other.&lt;/p&gt;
&lt;p&gt;This post will show you how you can do that in python using&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;&lt;a href=&#34;https://osmnx.readthedocs.io/en/stable/&#34;&gt;osmnx&lt;/a&gt;&lt;/em&gt; package to generate OSM road network&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href=&#34;https://networkx.org/documentation/stable/tutorial.html&#34;&gt;networkx&lt;/a&gt;&lt;/em&gt; package to find the nearest point on that network and calulate the walking distance between them&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href=&#34;http://udst.github.io/pandana/&#34;&gt;pandana&lt;/a&gt;&lt;/em&gt; package to make this significantly faster!&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;data&#34;&gt;Data&lt;/h2&gt;
&lt;p&gt;Availiable &lt;a href=&#34;https://github.com/lenkahas/sample_data/blob/main/points.geojson&#34;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;You might need some essential information that will help you to understand what we are doing here. Feel free to skip this part and go straight to the code.&lt;/p&gt;
&lt;h3 id=&#34;what-is-the-walking-distance&#34;&gt;What is the walking distance?&lt;/h3&gt;
&lt;p&gt;It&#39;s exactly what it says. Walking distance from one place to another. In other words, how far is the place from where I stand along the walking path. This information is crucial for estimating how long is it going to take me to go there, how much effort or money will this cost me. Those are all variables that contribute to estimations of accessibility, connectivity, mobility or others.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./pandana/Graphs2.png&#34; alt=&#34;graph2&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;estimating-the-walking-distance&#34;&gt;Estimating the walking distance.&lt;/h3&gt;
&lt;p&gt;The distance between two places/nodes is then the sum of the weights/lengths of all the paths/roads/edges that connects them.&lt;/p&gt;
&lt;p&gt;This can be also referred to as the &lt;a href=&#34;https://en.wikipedia.org/wiki/Shortest_path_problem&#34;&gt;Shortest Path Problem&lt;/a&gt;, in which we are looking for the most efficient paths between nodes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./pandana/Graph.png&#34; alt=&#34;graph&#34;&gt;&lt;/p&gt;
&lt;p&gt;In this example, the shortest path between Node 1 and Node 2 is the $$SUM(Weight 3, Weight 2, Weight 4)$$.
So if the weights represent a number of minutes (how long it takes to walk that path) then we would walk for 7 minutes.
$$ 1+2+4=7$$&lt;/p&gt;
&lt;p&gt;If would choose to go the other way, which is seemingly shorter,
$$SUM(Weight 1, Weight 4)$$
$$8+4=12$$
We would walk for 12 minutes. Therefore the first option is the most efficient one.&lt;/p&gt;
&lt;h3 id=&#34;find-the-nearest-neighbour&#34;&gt;Find the Nearest Neighbour&lt;/h3&gt;
&lt;p&gt;In any scenarios where the places you want to use for your analysis are not directly placed on the road or path, we need to connect the place to the network. We do this by finding the &lt;em&gt;Nearest neighbouring&lt;/em&gt; node on the graph/network to the specific place.
Both packages (networkx and pandana) have their own functions that can be used for these purposes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./pandana/Graphs3.png&#34; alt=&#34;graph&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;load-the-packages&#34;&gt;Load the packages&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import pandas as pd
import numpy as np
import geopandas as gpd
import matplotlib.pyplot as plt
import osmnx as ox # install osmnx first, it will download appropriate version of networkx
import networkx as nx
from pyproj import CRS
import itertools

import pandana
print(pandana.__version__)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define coordinates if you need them
wgs84 = CRS(4326)
bng = CRS(27700)
print(wgs84)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;epsg:4326
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;load-the-point-data-and-create-flows&#34;&gt;Load the point data and create flows&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;pois = gpd.read_file(&amp;quot;./points.geojson&amp;quot;)
#pois.crs # check the CRS
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;pois.head()
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
&lt;pre&gt;&lt;code&gt;.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/style&gt;&lt;/p&gt;
&lt;table border=&#34;1&#34; class=&#34;dataframe&#34;&gt;
  &lt;thead&gt;
    &lt;tr style=&#34;text-align: right;&#34;&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;ID_code&lt;/th&gt;
      &lt;th&gt;X&lt;/th&gt;
      &lt;th&gt;Y&lt;/th&gt;
      &lt;th&gt;geometry&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;0&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.502830&lt;/td&gt;
      &lt;td&gt;POINT (-2.55906 51.50283)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;A99931&lt;/td&gt;
      &lt;td&gt;-2.596140&lt;/td&gt;
      &lt;td&gt;51.459182&lt;/td&gt;
      &lt;td&gt;POINT (-2.59614 51.45918)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;A99986&lt;/td&gt;
      &lt;td&gt;-2.538833&lt;/td&gt;
      &lt;td&gt;51.483143&lt;/td&gt;
      &lt;td&gt;POINT (-2.53883 51.48314)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;L81002&lt;/td&gt;
      &lt;td&gt;-2.930352&lt;/td&gt;
      &lt;td&gt;51.360207&lt;/td&gt;
      &lt;td&gt;POINT (-2.93035 51.36021)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;-2.767317&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;POINT (-2.76732 51.48281)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# tale first 5 points and find all possinle combinations
pois = pois.iloc[0:6,:]

# create a list of all the ID&#39;s
p_li = list(pois[&#39;ID_code&#39;].unique())

# get all unique combinations of all the origins and destinations
flows = pd.DataFrame(list(itertools.product(p_li,p_li))).rename(columns = {0:&#39;origin&#39;,1:&#39;destination&#39;})

flows.info()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;class &#39;pandas.core.frame.DataFrame&#39;&amp;gt;
RangeIndex: 36 entries, 0 to 35
Data columns (total 2 columns):
 #   Column       Non-Null Count  Dtype 
---  ------       --------------  ----- 
 0   origin       36 non-null     object
 1   destination  36 non-null     object
dtypes: object(2)
memory usage: 704.0+ bytes
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;using-networkx&#34;&gt;Using Networkx&lt;/h2&gt;
&lt;h3 id=&#34;load-graph&#34;&gt;Load Graph&lt;/h3&gt;
&lt;p&gt;The osmnx package has very useful function &amp;lsquo;graph_from_bbox&amp;rsquo; which loads the osm roads graph quite quickly. Here I am loading walking paths for whola area of Avon where my points are located, which is quite a big area so expect this to take few minutes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create bounding box for our data 
bbox = [51.623985, 51.291124,  -2.272797, -3.029480] 

# generate graph based on the bounding box
avon = ox.graph_from_bbox(bbox[0], bbox[1], bbox[2], bbox[3], 
                               retain_all=False, 
                               truncate_by_edge=True, 
                               simplify=False,
                               network_type=&#39;walk&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# get the nodes and the edges into geopandas
nodes, edges = ox.graph_to_gdfs(avon, nodes=True, edges=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;find-the-nearest-node-to-each-point-with-osmnxget-nearest-node&#34;&gt;Find the nearest node to each point with osmnx.get_nearest_node()&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define function for the nearest neighbour
def nearest_node(a,b):
    nearest_node,dist=ox.get_nearest_node(avon, (a,b), return_dist=True, method = &#39;euclidean&#39;)  
    return nearest_node
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# apply the function
pois[&#39;NX_node&#39;] = np.vectorize(nearest_node)(pois[&#39;Y&#39;],pois[&#39;X&#39;])
pois.head()
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
&lt;pre&gt;&lt;code&gt;.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/style&gt;&lt;/p&gt;
&lt;table border=&#34;1&#34; class=&#34;dataframe&#34;&gt;
  &lt;thead&gt;
    &lt;tr style=&#34;text-align: right;&#34;&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;ID_code&lt;/th&gt;
      &lt;th&gt;X&lt;/th&gt;
      &lt;th&gt;Y&lt;/th&gt;
      &lt;th&gt;geometry&lt;/th&gt;
      &lt;th&gt;NX_node&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;0&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.502830&lt;/td&gt;
      &lt;td&gt;POINT (-2.55906 51.50283)&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;A99931&lt;/td&gt;
      &lt;td&gt;-2.596140&lt;/td&gt;
      &lt;td&gt;51.459182&lt;/td&gt;
      &lt;td&gt;POINT (-2.59614 51.45918)&lt;/td&gt;
      &lt;td&gt;1859320333&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;A99986&lt;/td&gt;
      &lt;td&gt;-2.538833&lt;/td&gt;
      &lt;td&gt;51.483143&lt;/td&gt;
      &lt;td&gt;POINT (-2.53883 51.48314)&lt;/td&gt;
      &lt;td&gt;2094196650&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;L81002&lt;/td&gt;
      &lt;td&gt;-2.930352&lt;/td&gt;
      &lt;td&gt;51.360207&lt;/td&gt;
      &lt;td&gt;POINT (-2.93035 51.36021)&lt;/td&gt;
      &lt;td&gt;317399984&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;-2.767317&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;POINT (-2.76732 51.48281)&lt;/td&gt;
      &lt;td&gt;2488740793&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create list of the node id&#39;s and check if they exist and if they are on the right place
nodelist = list(pois[&#39;NX_node&#39;].unique())

nodes[nodes.index.isin(nodelist)]
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
&lt;pre&gt;&lt;code&gt;.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/style&gt;&lt;/p&gt;
&lt;table border=&#34;1&#34; class=&#34;dataframe&#34;&gt;
  &lt;thead&gt;
    &lt;tr style=&#34;text-align: right;&#34;&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;y&lt;/th&gt;
      &lt;th&gt;x&lt;/th&gt;
      &lt;th&gt;street_count&lt;/th&gt;
      &lt;th&gt;highway&lt;/th&gt;
      &lt;th&gt;ref&lt;/th&gt;
      &lt;th&gt;geometry&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;osmid&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;317399984&lt;/th&gt;
      &lt;td&gt;51.360246&lt;/td&gt;
      &lt;td&gt;-2.930589&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;POINT (-2.93059 51.36025)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1859320333&lt;/th&gt;
      &lt;td&gt;51.459442&lt;/td&gt;
      &lt;td&gt;-2.596420&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;POINT (-2.59642 51.45944)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2094196650&lt;/th&gt;
      &lt;td&gt;51.482695&lt;/td&gt;
      &lt;td&gt;-2.538694&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;POINT (-2.53869 51.48270)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2424544775&lt;/th&gt;
      &lt;td&gt;51.413646&lt;/td&gt;
      &lt;td&gt;-2.571651&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;POINT (-2.57165 51.41365)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2488740793&lt;/th&gt;
      &lt;td&gt;51.483134&lt;/td&gt;
      &lt;td&gt;-2.767173&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;POINT (-2.76717 51.48313)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2914306154&lt;/th&gt;
      &lt;td&gt;51.502744&lt;/td&gt;
      &lt;td&gt;-2.558880&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;POINT (-2.55888 51.50274)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# get the node id&#39;s to flow data
# create two seperate data the target origins and target destinations with the XY coordinates
flows_full = flows.merge(pois.loc[:, pois.columns != &#39;geometry&#39;], left_on = &#39;origin&#39;, right_on = &#39;ID_code&#39;, how = &#39;left&#39;)

# lets just rename everything for the sake of clarity
flows_full = flows_full.merge(pois.loc[:, pois.columns != &#39;geometry&#39;], left_on = &#39;destination&#39;, right_on = &#39;ID_code&#39;, how = &#39;left&#39;).rename(columns = {&#39;NX_node_x&#39;:&#39;node_o&#39;,&#39;NX_node_y&#39;:&#39;node_d&#39;, &#39;X_x&#39;:&#39;X_o&#39;,&#39;Y_x&#39;:&#39;Y_o&#39;, &#39;X_y&#39;:&#39;X_d&#39;,&#39;Y_y&#39;:&#39;Y_d&#39;, &#39;ID_code_x&#39;:&#39;ID_code_o&#39;, &#39;ID_code_y&#39;:&#39;ID_code_d&#39;})

flows_full.head()
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
&lt;pre&gt;&lt;code&gt;.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/style&gt;&lt;/p&gt;
&lt;table border=&#34;1&#34; class=&#34;dataframe&#34;&gt;
  &lt;thead&gt;
    &lt;tr style=&#34;text-align: right;&#34;&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;origin&lt;/th&gt;
      &lt;th&gt;destination&lt;/th&gt;
      &lt;th&gt;ID_code_o&lt;/th&gt;
      &lt;th&gt;X_o&lt;/th&gt;
      &lt;th&gt;Y_o&lt;/th&gt;
      &lt;th&gt;node_o&lt;/th&gt;
      &lt;th&gt;ID_code_d&lt;/th&gt;
      &lt;th&gt;X_d&lt;/th&gt;
      &lt;th&gt;Y_d&lt;/th&gt;
      &lt;th&gt;node_d&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;0&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.502830&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A99931&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A99931&lt;/td&gt;
      &lt;td&gt;-2.596140&lt;/td&gt;
      &lt;td&gt;51.459182&lt;/td&gt;
      &lt;td&gt;1859320333&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A99986&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A99986&lt;/td&gt;
      &lt;td&gt;-2.538833&lt;/td&gt;
      &lt;td&gt;51.483143&lt;/td&gt;
      &lt;td&gt;2094196650&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;L81002&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;L81002&lt;/td&gt;
      &lt;td&gt;-2.930352&lt;/td&gt;
      &lt;td&gt;51.360207&lt;/td&gt;
      &lt;td&gt;317399984&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;-2.767317&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;2488740793&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&#34;check-that-the-nodes-are-on-the-corect-place&#34;&gt;Check that the nodes are on the corect place&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# check that the nodes are close to the points
plt.figure( figsize=(10,10))
fig = plt.plot()
ax = plt.axes()

nodes[nodes.index.isin(nodelist)].plot(ax=ax, markersize = 60, color=&amp;quot;b&amp;quot; )
pois.plot(ax=ax, markersize = 20, color=&amp;quot;r&amp;quot;)

plt.show();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./pandana/pandana_17_1.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Looks fine to me&lt;/p&gt;
&lt;h3 id=&#34;apply-the-networkxshortest-path-length&#34;&gt;Apply the networkx.shortest_path_length&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define a function that calculates shortest path between the nodes on graph
def path_length(row):
    return nx.shortest_path_length(avon, row[&#39;node_o&#39;], row[&#39;node_d&#39;], weight=&#39;length&#39;)

# apply the function to our OD data
%timeit flows_full[&#39;path_length&#39;] = flows_full.apply(path_length, axis=1)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;26.8 s ± 1.58 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# This is approximately
print(str(round((26.8*(7*1))/60,2)) + &#39; minutes&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;3.13 minutes
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;flows_full.head()
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
&lt;pre&gt;&lt;code&gt;.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/style&gt;&lt;/p&gt;
&lt;table border=&#34;1&#34; class=&#34;dataframe&#34;&gt;
  &lt;thead&gt;
    &lt;tr style=&#34;text-align: right;&#34;&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;origin&lt;/th&gt;
      &lt;th&gt;destination&lt;/th&gt;
      &lt;th&gt;ID_code_o&lt;/th&gt;
      &lt;th&gt;X_o&lt;/th&gt;
      &lt;th&gt;Y_o&lt;/th&gt;
      &lt;th&gt;node_o&lt;/th&gt;
      &lt;th&gt;ID_code_d&lt;/th&gt;
      &lt;th&gt;X_d&lt;/th&gt;
      &lt;th&gt;Y_d&lt;/th&gt;
      &lt;th&gt;node_d&lt;/th&gt;
      &lt;th&gt;path_length&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;0&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.502830&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;0.000&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A99931&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A99931&lt;/td&gt;
      &lt;td&gt;-2.596140&lt;/td&gt;
      &lt;td&gt;51.459182&lt;/td&gt;
      &lt;td&gt;1859320333&lt;/td&gt;
      &lt;td&gt;6960.514&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A99986&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A99986&lt;/td&gt;
      &lt;td&gt;-2.538833&lt;/td&gt;
      &lt;td&gt;51.483143&lt;/td&gt;
      &lt;td&gt;2094196650&lt;/td&gt;
      &lt;td&gt;4333.457&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;L81002&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;L81002&lt;/td&gt;
      &lt;td&gt;-2.930352&lt;/td&gt;
      &lt;td&gt;51.360207&lt;/td&gt;
      &lt;td&gt;317399984&lt;/td&gt;
      &lt;td&gt;36334.316&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;-2.767317&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;2488740793&lt;/td&gt;
      &lt;td&gt;19754.732&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 id=&#34;using-pandana&#34;&gt;Using Pandana&lt;/h2&gt;
&lt;h3 id=&#34;load-the-graph&#34;&gt;Load the graph&lt;/h3&gt;
&lt;p&gt;Listen, I have tried to use the osm loader inside the pandana, but it took ages. after 2 hours of loading, I decided it will be easier to get the previously loaded osm graph with osmnx and use that as a base for the pandana graph. You can just simply use the geodataframes of nodes and flows for the base of your graph.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# reset index so our origins and destinations are not in idnex
edges = edges.reset_index()

# create network with pandana
avon_pan = pandana.Network(nodes[&#39;x&#39;], nodes[&#39;y&#39;], 
                          edges[&#39;u&#39;], edges[&#39;v&#39;], edges[[&#39;length&#39;]])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;find-the-nearest-nodes-to-our-points-in-pandana-graph&#34;&gt;Find the nearest nodes to our points in pandana graph&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# we are going to define the origins and destinations separately as alist
# get_node_ids uses kdtree writen in c++, google it its like a magic

origin_nodes = avon_pan.get_node_ids(flows_full.X_o, flows_full.Y_o).values


dests_nodes = avon_pan.get_node_ids(flows_full.X_d, flows_full.Y_d).values
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;flows_full.head()
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
&lt;pre&gt;&lt;code&gt;.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/style&gt;&lt;/p&gt;
&lt;table border=&#34;1&#34; class=&#34;dataframe&#34;&gt;
  &lt;thead&gt;
    &lt;tr style=&#34;text-align: right;&#34;&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;origin&lt;/th&gt;
      &lt;th&gt;destination&lt;/th&gt;
      &lt;th&gt;ID_code_o&lt;/th&gt;
      &lt;th&gt;X_o&lt;/th&gt;
      &lt;th&gt;Y_o&lt;/th&gt;
      &lt;th&gt;node_o&lt;/th&gt;
      &lt;th&gt;ID_code_d&lt;/th&gt;
      &lt;th&gt;X_d&lt;/th&gt;
      &lt;th&gt;Y_d&lt;/th&gt;
      &lt;th&gt;node_d&lt;/th&gt;
      &lt;th&gt;path_length&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;0&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.502830&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;0.000&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A99931&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A99931&lt;/td&gt;
      &lt;td&gt;-2.596140&lt;/td&gt;
      &lt;td&gt;51.459182&lt;/td&gt;
      &lt;td&gt;1859320333&lt;/td&gt;
      &lt;td&gt;6960.514&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A99986&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A99986&lt;/td&gt;
      &lt;td&gt;-2.538833&lt;/td&gt;
      &lt;td&gt;51.483143&lt;/td&gt;
      &lt;td&gt;2094196650&lt;/td&gt;
      &lt;td&gt;4333.457&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;L81002&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;L81002&lt;/td&gt;
      &lt;td&gt;-2.930352&lt;/td&gt;
      &lt;td&gt;51.360207&lt;/td&gt;
      &lt;td&gt;317399984&lt;/td&gt;
      &lt;td&gt;36334.316&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;-2.767317&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;2488740793&lt;/td&gt;
      &lt;td&gt;19754.732&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&#34;calculate-distances-using-pandana-shortest-path-lengths&#34;&gt;Calculate distances using pandana &amp;lsquo;shortest_path_lengths&amp;rsquo;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;%%time
flows_full[&#39;distances&#39;] = pd.Series(avon_pan.shortest_path_lengths(origin_nodes, dests_nodes))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Wall time: 144 ms
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# How much faster is this from networkx
(((3.13*60)*1000))/(144)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;1304.1666666666665
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is more than 1000times faster!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;flows_full.head()
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;style scoped&gt;
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }
&lt;pre&gt;&lt;code&gt;.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/style&gt;&lt;/p&gt;
&lt;table border=&#34;1&#34; class=&#34;dataframe&#34;&gt;
  &lt;thead&gt;
    &lt;tr style=&#34;text-align: right;&#34;&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;origin&lt;/th&gt;
      &lt;th&gt;destination&lt;/th&gt;
      &lt;th&gt;ID_code_o&lt;/th&gt;
      &lt;th&gt;X_o&lt;/th&gt;
      &lt;th&gt;Y_o&lt;/th&gt;
      &lt;th&gt;node_o&lt;/th&gt;
      &lt;th&gt;ID_code_d&lt;/th&gt;
      &lt;th&gt;X_d&lt;/th&gt;
      &lt;th&gt;Y_d&lt;/th&gt;
      &lt;th&gt;node_d&lt;/th&gt;
      &lt;th&gt;path_length&lt;/th&gt;
      &lt;th&gt;distances&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;0&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.502830&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;0.000&lt;/td&gt;
      &lt;td&gt;0.000&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A99931&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A99931&lt;/td&gt;
      &lt;td&gt;-2.596140&lt;/td&gt;
      &lt;td&gt;51.459182&lt;/td&gt;
      &lt;td&gt;1859320333&lt;/td&gt;
      &lt;td&gt;6960.514&lt;/td&gt;
      &lt;td&gt;6960.508&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;A99986&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;A99986&lt;/td&gt;
      &lt;td&gt;-2.538833&lt;/td&gt;
      &lt;td&gt;51.483143&lt;/td&gt;
      &lt;td&gt;2094196650&lt;/td&gt;
      &lt;td&gt;4333.457&lt;/td&gt;
      &lt;td&gt;4333.454&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;L81002&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;L81002&lt;/td&gt;
      &lt;td&gt;-2.930352&lt;/td&gt;
      &lt;td&gt;51.360207&lt;/td&gt;
      &lt;td&gt;317399984&lt;/td&gt;
      &lt;td&gt;36334.316&lt;/td&gt;
      &lt;td&gt;36334.302&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;A91120&lt;/td&gt;
      &lt;td&gt;-2.559063&lt;/td&gt;
      &lt;td&gt;51.50283&lt;/td&gt;
      &lt;td&gt;2914306154&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;-2.767317&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;2488740793&lt;/td&gt;
      &lt;td&gt;19754.732&lt;/td&gt;
      &lt;td&gt;19754.729&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The distances seems to be satisfying but are we sure that the packages picked up the same nodes?&lt;/p&gt;
&lt;h3 id=&#34;check-that-the-nodes-are-the-same-as-those-from-networkx&#34;&gt;Check that the nodes are the same as those from NetworkX&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;x = origin_nodes == flows_full[&#39;node_o&#39;]
x.describe()

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;count       36
unique       1
top       True
freq        36
Name: node_o, dtype: object
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;y = dests_nodes == flows_full[&#39;node_d&#39;]
y.describe()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;count       36
unique       1
top       True
freq        36
Name: node_d, dtype: object
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks spot on.&lt;/p&gt;
&lt;h2 id=&#34;appendix&#34;&gt;Appendix&lt;/h2&gt;
&lt;h3 id=&#34;google-colab&#34;&gt;Google Colab&lt;/h3&gt;
&lt;p&gt;If you want to run this on Google Colab, you need to install&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Geopandas&lt;/li&gt;
&lt;li&gt;OsmnX&lt;/li&gt;
&lt;li&gt;Matplotlib&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# install Geopandas
# Important library for many geopython libraries
!apt install gdal-bin python-gdal python3-gdal 
# Install rtree - Geopandas requirment
!apt install python3-rtree 
# Install Geopandas
!pip install git+git://github.com/geopandas/geopandas.git
# Install descartes - Geopandas requirment
!pip install descartes 
# Install Folium for Geographic data visualization
!pip install folium
# Install plotlyExpress
!pip install plotly_express# install packages
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# osmnx
# this gives errors but works ...no idea
!apt-get -qq install -y libspatialindex-dev &amp;amp;&amp;amp; pip install -q -U osmnx 
ox.config(use_cache=True, log_console=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;!python -m pip uninstall matplotlib
!pip install matplotlib==3.1.3# mount the drive
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;environment-set-up&#34;&gt;Environment set up&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;name: graphs
channels:
  - conda-forge
  - defaults
dependencies:
  - _r-mutex=1.0.1=anacondar_1
  - anyio=2.2.0=py39hcbf5309_0
  - argon2-cffi=20.1.0=py39hb82d6ee_2
  - async_generator=1.10=py_0
  - attrs=20.3.0=pyhd3deb0d_0
  - babel=2.9.0=pyhd3deb0d_0
  - backcall=0.2.0=pyh9f0ad1d_0
  - backports=1.0=py_2
  - backports.functools_lru_cache=1.6.1=py_0
  - bleach=3.3.0=pyh44b312d_0
  - blosc=1.21.0=h0e60522_0
  - boost-cpp=1.74.0=h54f0996_2
  - branca=0.4.2=pyhd8ed1ab_0
  - brotlipy=0.7.0=py39hb82d6ee_1001
  - bzip2=1.0.8=h8ffe710_4
  - ca-certificates=2020.12.5=h5b45459_0
  - cairo=1.16.0=hba8bd2f_1007
  - certifi=2020.12.5=py39hcbf5309_1
  - cffi=1.14.5=py39h0878f49_0
  - cfitsio=3.470=h0af3d06_7
  - chardet=4.0.0=py39hcbf5309_1
  - click=7.1.2=pyh9f0ad1d_0
  - click-plugins=1.1.1=py_0
  - cligj=0.7.1=pyhd8ed1ab_0
  - colorama=0.4.4=pyh9f0ad1d_0
  - cryptography=3.4.6=py39hd8d06c1_0
  - curl=7.75.0=hf1763fc_0
  - cycler=0.10.0=py_2
  - decorator=4.4.2=py_0
  - defusedxml=0.7.1=pyhd8ed1ab_0
  - descartes=1.1.0=py_4
  - entrypoints=0.3=pyhd8ed1ab_1003
  - expat=2.2.10=h39d44d4_0
  - fiona=1.8.18=py39h9f1b043_1
  - folium=0.12.0=pyhd8ed1ab_0
  - fontconfig=2.13.1=h1989441_1004
  - freetype=2.10.4=h546665d_1
  - freexl=1.0.5=hd288d7e_1002
  - gdal=3.2.2=py39h6795fcd_0
  - geopandas=0.9.0=pyhd8ed1ab_0
  - geos=3.9.1=h39d44d4_2
  - geotiff=1.6.0=h8e90983_5
  - gettext=0.19.8.1=h1a89ca6_1005
  - hdf4=4.2.13=h0e5069d_1004
  - hdf5=1.10.6=nompi_h5268f04_1114
  - icu=68.1=h0e60522_0
  - idna=2.10=pyh9f0ad1d_0
  - importlib-metadata=3.7.2=py39hcbf5309_0
  - intel-openmp=2020.3=h57928b3_311
  - ipykernel=5.5.0=py39h832f523_1
  - ipython=7.21.0=py39h832f523_0
  - ipython_genutils=0.2.0=py_1
  - jedi=0.18.0=py39hcbf5309_2
  - jinja2=2.11.3=pyh44b312d_0
  - joblib=1.0.1=pyhd8ed1ab_0
  - jpeg=9d=h8ffe710_0
  - json5=0.9.5=pyh9f0ad1d_0
  - jsonschema=3.2.0=pyhd8ed1ab_3
  - jupyter-packaging=0.7.12=pyhd8ed1ab_0
  - jupyter_client=6.1.11=pyhd8ed1ab_1
  - jupyter_core=4.7.1=py39hcbf5309_0
  - jupyter_server=1.4.1=py39hcbf5309_0
  - jupyterlab=3.0.10=pyhd8ed1ab_0
  - jupyterlab_pygments=0.1.2=pyh9f0ad1d_0
  - jupyterlab_server=2.3.0=pyhd8ed1ab_0
  - kealib=1.4.14=h96bfa42_2
  - kiwisolver=1.3.1=py39h2e07f2f_1
  - krb5=1.17.2=hbae68bd_0
  - lcms2=2.12=h2a16943_0
  - libblas=3.9.0=8_mkl
  - libcblas=3.9.0=8_mkl
  - libcurl=7.75.0=hf1763fc_0
  - libffi=3.3=h0e60522_2
  - libgdal=3.2.2=hbe61683_0
  - libglib=2.66.7=h1e62bf3_1
  - libiconv=1.16=he774522_0
  - libkml=1.3.0=h02ac0ef_1012
  - liblapack=3.9.0=8_mkl
  - libnetcdf=4.7.4=nompi_h3a9aa94_107
  - libpng=1.6.37=h1d00b33_2
  - libpq=13.1=h4f54205_2
  - librttopo=1.1.0=hb340de5_6
  - libsodium=1.0.18=h8d14728_1
  - libspatialindex=1.9.3=h39d44d4_3
  - libspatialite=5.0.1=h6b539a6_4
  - libssh2=1.9.0=h680486a_6
  - libtiff=4.2.0=hc10be44_0
  - libwebp-base=1.2.0=h8ffe710_0
  - libxml2=2.9.10=hf5bbc77_3
  - lz4-c=1.9.3=h8ffe710_0
  - m2w64-bwidget=1.9.10=2
  - m2w64-bzip2=1.0.6=6
  - m2w64-expat=2.1.1=2
  - m2w64-fftw=3.3.4=6
  - m2w64-flac=1.3.1=3
  - m2w64-gcc-libgfortran=5.3.0=6
  - m2w64-gcc-libs=5.3.0=7
  - m2w64-gcc-libs-core=5.3.0=7
  - m2w64-gettext=0.19.7=2
  - m2w64-gmp=6.1.0=2
  - m2w64-gsl=2.1=2
  - m2w64-libiconv=1.14=6
  - m2w64-libjpeg-turbo=1.4.2=3
  - m2w64-libogg=1.3.2=3
  - m2w64-libpng=1.6.21=2
  - m2w64-libsndfile=1.0.26=2
  - m2w64-libtiff=4.0.6=2
  - m2w64-libvorbis=1.3.5=2
  - m2w64-libwinpthread-git=5.0.0.4634.697f757=2
  - m2w64-libxml2=2.9.3=3
  - m2w64-mpfr=3.1.4=4
  - m2w64-pcre2=10.34=0
  - m2w64-speex=1.2rc2=3
  - m2w64-speexdsp=1.2rc3=3
  - m2w64-tcl=8.6.5=3
  - m2w64-tk=8.6.5=3
  - m2w64-tktable=2.10=5
  - m2w64-wineditline=2.101=5
  - m2w64-xz=5.2.2=2
  - m2w64-zlib=1.2.8=10
  - markupsafe=1.1.1=py39hb82d6ee_3
  - matplotlib-base=3.3.4=py39h581301d_0
  - mistune=0.8.4=py39hb82d6ee_1003
  - mkl=2020.4=hb70f87d_311
  - mock=4.0.3=py39hcbf5309_1
  - msys2-conda-epoch=20160418=1
  - munch=2.5.0=py_0
  - nbclassic=0.2.6=pyhd8ed1ab_0
  - nbclient=0.5.3=pyhd8ed1ab_0
  - nbconvert=6.0.7=py39hcbf5309_3
  - nbformat=5.1.2=pyhd8ed1ab_1
  - nest-asyncio=1.4.3=pyhd8ed1ab_0
  - networkx=2.5=py_0
  - notebook=6.2.0=py39hcbf5309_0
  - numexpr=2.7.3=py39h2e25243_0
  - numpy=1.20.1=py39h6635163_0
  - olefile=0.46=pyh9f0ad1d_1
  - openjpeg=2.4.0=h48faf41_0
  - openssl=1.1.1j=h8ffe710_0
  - osmnx=1.0.1=pyhd3deb0d_0
  - packaging=20.9=pyh44b312d_0
  - pandana=0.6=py39h2e25243_0
  - pandas=1.2.3=py39h2e25243_0
  - pandoc=2.12=h8ffe710_0
  - pandocfilters=1.4.2=py_1
  - parso=0.8.1=pyhd8ed1ab_0
  - pcre=8.44=ha925a31_0
  - pickleshare=0.7.5=py_1003
  - pillow=8.1.2=py39h1a9d4f7_0
  - pip=21.0.1=pyhd8ed1ab_0
  - pixman=0.40.0=h8ffe710_0
  - poppler=21.03.0=h9ff6ed8_0
  - poppler-data=0.4.10=0
  - postgresql=13.1=h0f1a9bc_2
  - proj=8.0.0=h1cfcee9_0
  - prometheus_client=0.9.0=pyhd3deb0d_0
  - prompt-toolkit=3.0.16=pyha770c72_0
  - pycparser=2.20=pyh9f0ad1d_2
  - pygments=2.8.1=pyhd8ed1ab_0
  - pyopenssl=20.0.1=pyhd8ed1ab_0
  - pyparsing=2.4.7=pyh9f0ad1d_0
  - pyproj=3.0.1=py39h1007a03_1
  - pyrsistent=0.17.3=py39hb82d6ee_2
  - pysocks=1.7.1=py39hcbf5309_3
  - pytables=3.6.1=py39h42e6cd8_3
  - python=3.9.2=h7840368_0_cpython
  - python-dateutil=2.8.1=py_0
  - python_abi=3.9=1_cp39
  - pytz=2021.1=pyhd8ed1ab_0
  - pywin32=300=py39hb82d6ee_0
  - pywinpty=0.5.7=py39hde42818_1
  - pyzmq=22.0.3=py39he46f08e_1
  - r-base=4.0.3=hddad469_7
  - r-iterators=1.0.13=r40h142f84f_0
  - r-itertools=0.1_3=r40_1003
  - requests=2.25.1=pyhd3deb0d_0
  - rtree=0.9.7=py39h09fdee3_1
  - scikit-learn=0.24.1=py39he931e04_0
  - scipy=1.6.0=py39hc0c34ad_0
  - send2trash=1.5.0=py_0
  - setuptools=49.6.0=py39hcbf5309_3
  - shapely=1.7.1=py39h90c6b7e_4
  - six=1.15.0=pyh9f0ad1d_0
  - sniffio=1.2.0=py39hcbf5309_1
  - sqlite=3.34.0=h8ffe710_0
  - terminado=0.9.2=py39hcbf5309_0
  - testpath=0.4.4=py_0
  - threadpoolctl=2.1.0=pyh5ca1d4c_0
  - tiledb=2.2.4=hddc2a84_2
  - tk=8.6.10=h8ffe710_1
  - tornado=6.1=py39hb82d6ee_1
  - traitlets=5.0.5=py_0
  - tzdata=2021a=he74cb21_0
  - urllib3=1.26.3=pyhd8ed1ab_0
  - vc=14.2=hb210afc_4
  - vs2015_runtime=14.28.29325=h5e1d092_4
  - wcwidth=0.2.5=pyh9f0ad1d_2
  - webencodings=0.5.1=py_1
  - wheel=0.36.2=pyhd3deb0d_0
  - win_inet_pton=1.1.0=py39hcbf5309_2
  - wincertstore=0.2=py39hcbf5309_1006
  - winpty=0.4.3=4
  - xerces-c=3.2.3=h0e60522_2
  - xz=5.2.5=h62dcd97_1
  - zeromq=4.3.4=h0e60522_0
  - zipp=3.4.1=pyhd8ed1ab_0
  - zlib=1.2.11=h62dcd97_1010
  - zstd=1.4.9=h6255e5f_0

&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
  </channel>
</rss>
