<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Distance Decay | Lenka&#39;s blog</title>
    <link>https://lenkastatham.com/tags/distance-decay.html</link>
      <atom:link href="https://lenkastatham.com/tags/distance-decay/index.xml" rel="self" type="application/rss+xml" />
    <description>Distance Decay</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-gb</language><lastBuildDate>Wed, 18 May 2022 00:00:00 +0000</lastBuildDate>
    <image>
      <url>https://lenkastatham.com/img/icon-192.png</url>
      <title>Distance Decay</title>
      <link>https://lenkastatham.com/tags/distance-decay.html</link>
    </image>
    
    <item>
      <title>Proximity and Distance Decay, chapter in GIS&amp;T Body of Knowledge</title>
      <link>https://lenkastatham.com/publication/gist2022.html</link>
      <pubDate>Wed, 18 May 2022 00:00:00 +0000</pubDate>
      <guid>https://lenkastatham.com/publication/gist2022.html</guid>
      <description></description>
    </item>
    
    <item>
      <title>Modelling Urban Flows, Spatial Effects in Origin-Destination data</title>
      <link>https://lenkastatham.com/publication/gisruk2020.html</link>
      <pubDate>Wed, 22 Jul 2020 00:00:00 +0000</pubDate>
      <guid>https://lenkastatham.com/publication/gisruk2020.html</guid>
      <description></description>
    </item>
    
    <item>
      <title>Application of XGBoost regression for Spatial Interaction of Urban flow</title>
      <link>https://lenkastatham.com/post/xgboost2.html</link>
      <pubDate>Wed, 27 May 2020 00:00:00 +0000</pubDate>
      <guid>https://lenkastatham.com/post/xgboost2.html</guid>
      <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;This notebook provides a simple example of the application of XGBoost Spatial Interaction model in python. For the purposes of this tutorial I will expect you to have some knowledge of Spatial Interaction Models and their purpose as well as some python skills. Yet, if you want to refresh a little or wish to learn, here is a list of relevant sources.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Watch Maarten Vanhoof talking about what are the spatial Interaction Models and how they can be used &lt;a href=&#34;https://www.youtube.com/watch?v=3wzaBNQCAc0&#34;&gt;here&lt;/a&gt; (6 min)&lt;/li&gt;
&lt;li&gt;Have a look at a list of some relevant literature in different fields &lt;a href=&#34;https://www.sciencedirect.com/topics/computer-science/spatial-interaction-model&#34;&gt;here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Look at the example of Spatial Interaction Model application in R from Adam Dennett &lt;a href=&#34;https://rpubs.com/adam_dennett/257231&#34;&gt;Part1&lt;/a&gt; and &lt;a href=&#34;https://rpubs.com/adam_dennett/259068&#34;&gt;Part2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Look at how you can apply the the same method in python with &lt;a href=&#34;https://github.com/pysal/spint&#34;&gt;SpInt package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;so-what-is-the-xgboost-and-how-does-it-work&#34;&gt;So what is the XGBoost? and how does it work?&lt;/h2&gt;
&lt;p&gt;XGBoost stands for “Extreme Gradient Boosting”, where the term “Gradient Boosting” originates from the paper &lt;a href=&#34;https://statweb.stanford.edu/~jhf/ftp/trebst.pdf&#34;&gt;Greedy Function Approximation: A Gradient Boosting Machine, by Friedman&lt;/a&gt;. Please have a look at this &lt;a href=&#34;https://machinelearningmastery.com/gentle-introduction-xgboost-applied-machine-learning/&#34;&gt;A Gentle Introduction to XGBoost for Applied Machine Learning&lt;/a&gt; and the &lt;a href=&#34;https://xgboost.readthedocs.io/en/latest/&#34;&gt;Full documentation to XGBoost&lt;/a&gt; that also provides a tutorials. If you are like me and need something more visual and efficient explanation of the how the model works, I would highly recommend YouTube videos from Josh Starmer;
&lt;a href=&#34;https://www.youtube.com/watch?v=3CC4N4z3GJc&amp;amp;list=TLPQMTYwNDIwMjCcfB4rXzVabw&amp;amp;index=1&#34;&gt;Gradient Boost Part 1: Regression Main Ideas&lt;/a&gt;, &lt;a href=&#34;https://www.youtube.com/watch?v=2xudPOBz-vs&#34;&gt;Gradient Boost Part 2: Regression Details&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So after you gone through all the resources you know that there are two types of the model depending on what do you want to do with them. You can use them as a &lt;strong&gt;classifier&lt;/strong&gt;, similar to decision trees, or as a &lt;strong&gt;regressor&lt;/strong&gt;. Using the method as a classifier, we can predict discrete and categorical variables, and using it as a regressors, continuous variables can be predicted. Here we will be talking solely about Gradient Boosting as a regressor, because we are trying to model Urban flow, number of people that moves from one point to another.&lt;/p&gt;
&lt;h3 id=&#34;what-is-an-urban-flow-here&#34;&gt;What is an Urban flow here?&lt;/h3&gt;
&lt;p&gt;In this example, I am looking at patients Urban flow. to be specific, this is a patients that are being treated at particular GP, receive a prescription and then go to one of the pharmacies to pick up the prescribed drug. This process of prescribing and dispensing a drug is closely monitored by the National Health Service, although the precise information about the patients is disclosed, the NHS Digital provides completely anonymised data of number of the prescriptions that has been prescribed and dispensed between each pair of GP and Pharmacy each month. Those can be translated as traces of patients movement between two points and together creates rich network of patients flow.&lt;/p&gt;
&lt;p&gt;For further description of the data follow up on &lt;a href=&#34;&#34;&gt;this&lt;/a&gt; notebook.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/GPPH.png&#34; alt=&#34;Patients flow diagram&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;cross-validation-challenge&#34;&gt;Cross validation challenge&lt;/h3&gt;
&lt;p&gt;With any ordinary data, we would usually divide the data into testing set and training set by 80/20 split or any other proportion. However, we face more difficult case here.&lt;/p&gt;
&lt;p&gt;The Urban flow networks are very particular case of network, which is basically a representation of the human behaviour in a space. And this is related to anything that affect our decision to choose a destination and the journey to it. Starting from maps, road or public transport structure and the type of vehicle, ending with our personal preference, gender, or family status. The network that the humans created by acting in it has very complex pattern in which the existence of a destination and its location matters.&lt;/p&gt;
&lt;p&gt;Now, imagine we would divide the dataset into training and testing, how do we decide which locations will be for training and which ones will be for testing if each location is unique? For example, if we train the model on the flow data in a Scotland, we cannot test the same model on the data from London as people in both places has different behavioural patterns. There is more to it, read through &lt;a href=&#34;https://onlinelibrary.wiley.com/doi/10.1111/ecog.02881&#34;&gt;this study&lt;/a&gt; that provides nice overview.&lt;/p&gt;
&lt;p&gt;Luckily, the data I collected ranges from 2013 to mid-2019, which means that we can divide the data into blocks with the same timeframe. In this case I will use the data from 2017 for training and data from 2018 for testing. This block cross validation is only possible thanks to the consistent data.&lt;/p&gt;
&lt;h2 id=&#34;lets-get-started&#34;&gt;Let&#39;s get started&lt;/h2&gt;
&lt;h3 id=&#34;load-the-libraries&#34;&gt;Load the libraries&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import xgboost as xgb # xgboost model library
import numpy as np # array manipulation
import pandas as pd # dataframe manipulation library
import geopandas as gpd # spatial data frame manipulatioon library
import statsmodels.api as sm
from sklearn.model_selection import train_test_split  # take out the validation packages from scikit.learn
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt # plotting 
import seaborn as sns
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;load-the-2017-train-data&#34;&gt;Load the 2017 train data&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define the path
file = r&#39;./../.././data/NHS/working/year_data_flow/predicted_flow_2017.csv&#39;
# read the flow file
fl17 = pd.read_csv(file, index_col=None)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define the path
file = r&#39;./../.././data/NHS/working/year_data_flow/complete_predicted_flow_2017.csv&#39;
# read the flow file
cfl17 = pd.read_csv(file, index_col=None)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;fl17.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;P_Code&lt;/th&gt;
      &lt;th&gt;P_Name_x&lt;/th&gt;
      &lt;th&gt;P_Postcode&lt;/th&gt;
      &lt;th&gt;D_Code_x&lt;/th&gt;
      &lt;th&gt;D_Name_x&lt;/th&gt;
      &lt;th&gt;D_Postcode&lt;/th&gt;
      &lt;th&gt;N_Items&lt;/th&gt;
      &lt;th&gt;N_EPSItems&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;loop&lt;/th&gt;
      &lt;th&gt;...&lt;/th&gt;
      &lt;th&gt;P_full_address_y&lt;/th&gt;
      &lt;th&gt;P_Status_Code_y&lt;/th&gt;
      &lt;th&gt;P_Sub_type_y&lt;/th&gt;
      &lt;th&gt;P_Setting_y&lt;/th&gt;
      &lt;th&gt;P_X_y&lt;/th&gt;
      &lt;th&gt;P_Y_y&lt;/th&gt;
      &lt;th&gt;CODE&lt;/th&gt;
      &lt;th&gt;POSTCODE&lt;/th&gt;
      &lt;th&gt;NUMBER_OF_PATIENTS&lt;/th&gt;
      &lt;th&gt;yhat&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;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FA251&lt;/td&gt;
      &lt;td&gt;HORIZON PHARMACY LTD&lt;/td&gt;
      &lt;td&gt;BS34 6AS&lt;/td&gt;
      &lt;td&gt;130.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP,PORTISHEAD MEDICAL GR...&lt;/td&gt;
      &lt;td&gt;A&lt;/td&gt;
      &lt;td&gt;B&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;-2.767318&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;18363&lt;/td&gt;
      &lt;td&gt;0.044989&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FA613&lt;/td&gt;
      &lt;td&gt;DOWNHAM FB LTD&lt;/td&gt;
      &lt;td&gt;BS7 9JT&lt;/td&gt;
      &lt;td&gt;39.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP,PORTISHEAD MEDICAL GR...&lt;/td&gt;
      &lt;td&gt;A&lt;/td&gt;
      &lt;td&gt;B&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;-2.767318&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;18363&lt;/td&gt;
      &lt;td&gt;0.240777&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FCL91&lt;/td&gt;
      &lt;td&gt;MYGLOBE LTD&lt;/td&gt;
      &lt;td&gt;BS15 4ND&lt;/td&gt;
      &lt;td&gt;12.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP,PORTISHEAD MEDICAL GR...&lt;/td&gt;
      &lt;td&gt;A&lt;/td&gt;
      &lt;td&gt;B&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;-2.767318&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;18363&lt;/td&gt;
      &lt;td&gt;0.001761&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FCT35&lt;/td&gt;
      &lt;td&gt;LLOYDS PHARMACY LTD&lt;/td&gt;
      &lt;td&gt;BS20 7QA&lt;/td&gt;
      &lt;td&gt;661.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP,PORTISHEAD MEDICAL GR...&lt;/td&gt;
      &lt;td&gt;A&lt;/td&gt;
      &lt;td&gt;B&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;-2.767318&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;18363&lt;/td&gt;
      &lt;td&gt;2393.873899&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;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FDD77&lt;/td&gt;
      &lt;td&gt;JOHN WARE LIMITED&lt;/td&gt;
      &lt;td&gt;BS20 6LT&lt;/td&gt;
      &lt;td&gt;4058.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP,PORTISHEAD MEDICAL GR...&lt;/td&gt;
      &lt;td&gt;A&lt;/td&gt;
      &lt;td&gt;B&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;-2.767318&lt;/td&gt;
      &lt;td&gt;51.482805&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;18363&lt;/td&gt;
      &lt;td&gt;2149.320471&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;5 rows × 77 columns&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&#34;load-the-2018-test-data&#34;&gt;Load the 2018 test data&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# first get the 018 data in, those will be the test data
file = r&#39;./../.././data/NHS/working/year_data_flow/predicted_flow_2018.csv&#39;
# read the flow file
fl18 = pd.read_csv(file, index_col=None)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define the path
file = r&#39;./../.././data/NHS/working/year_data_flow/complete_predicted_flow_2018.csv&#39;
# read the flow file
cfl18 = pd.read_csv(file, index_col=None)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;cfl18.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;P_Code&lt;/th&gt;
      &lt;th&gt;D_Code_new&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;Density&lt;/th&gt;
      &lt;th&gt;NUMBER_OF_PATIENTS&lt;/th&gt;
      &lt;th&gt;N_Items&lt;/th&gt;
      &lt;th&gt;Euc_distance&lt;/th&gt;
      &lt;th&gt;great_circle_dist&lt;/th&gt;
      &lt;th&gt;predicted&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;L81004&lt;/td&gt;
      &lt;td&gt;FA696&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;36.696000&lt;/td&gt;
      &lt;td&gt;18615&lt;/td&gt;
      &lt;td&gt;8.0&lt;/td&gt;
      &lt;td&gt;0.224446&lt;/td&gt;
      &lt;td&gt;16.560437&lt;/td&gt;
      &lt;td&gt;0.000777&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;FCT35&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;34.918367&lt;/td&gt;
      &lt;td&gt;18615&lt;/td&gt;
      &lt;td&gt;21.0&lt;/td&gt;
      &lt;td&gt;0.009198&lt;/td&gt;
      &lt;td&gt;0.653229&lt;/td&gt;
      &lt;td&gt;1963.680982&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;FDD77&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;29.092308&lt;/td&gt;
      &lt;td&gt;18615&lt;/td&gt;
      &lt;td&gt;5309.0&lt;/td&gt;
      &lt;td&gt;0.014101&lt;/td&gt;
      &lt;td&gt;0.983999&lt;/td&gt;
      &lt;td&gt;1673.379704&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;FD125&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;28.670000&lt;/td&gt;
      &lt;td&gt;18615&lt;/td&gt;
      &lt;td&gt;26.0&lt;/td&gt;
      &lt;td&gt;0.177172&lt;/td&gt;
      &lt;td&gt;12.816035&lt;/td&gt;
      &lt;td&gt;0.030142&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;FEA59&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;30.833333&lt;/td&gt;
      &lt;td&gt;18615&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.292934&lt;/td&gt;
      &lt;td&gt;20.302017&lt;/td&gt;
      &lt;td&gt;0.000028&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;I have already selected the columns we need for the analysis but let me provide you with some explanation.&lt;/p&gt;
&lt;p&gt;The gravity equation says that the flow, can be explained by gravitational power of two masses on origin and destination, and inversely proportional to the distance between them.&lt;/p&gt;
&lt;p&gt;$$ F_{od} = k\frac{M_o M_d}{D_{od}} $$&lt;/p&gt;
&lt;p&gt;Where $F_{od}$ is the flow mass, $M_o$ is the mass on the origin, $M_d$ is mass on the destination and $D_{od}$ is a distance between them.&lt;/p&gt;
&lt;p&gt;In our case the flow is the number of patient&#39;s between the origin (General Practitioner) and the destination (Pharmacy). The mass of the origin is represented by the number of patients registered at that particular GP in that year (mid-year estimate). The mass of the destination is the average daytime population in 1km radius from the pharmacy. And the distance is a great circle distance between the origin and destination. All of those has been calculated previously and I&#39;m going to bore you with it another time.&lt;/p&gt;
&lt;p&gt;In our datasets this is;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;Variable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$F_{od}$&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;&amp;lsquo;N_Items&amp;rsquo;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$M_o$&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;&amp;lsquo;NUMBER_OF_PATIENTS&amp;rsquo;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$M_d$&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;&amp;lsquo;Density&amp;rsquo;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$D_{od}$&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;&amp;lsquo;great_circle_dist&amp;rsquo;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;There are different spatial interaction models but for this purpose we only want to stick with the very basic form, one based on gravity model. We just need to translate the formula into an regression, Poisson regression to be more specific, because the flow is a count of the patients and skewed dis&lt;/p&gt;
&lt;h2 id=&#34;spatial-interaction-model-definition&#34;&gt;Spatial Interaction model definition&lt;/h2&gt;
&lt;p&gt;$$F_{od} = \exp( k + \mu  \ln{M_o} + \alpha  \ln{M_d} − \beta  \ln{ D_{od}} )$$&lt;/p&gt;
&lt;p&gt;Where $\lambda_{od}$ is the flow from $o$ to $d$, logarithmically linked or modelled by a linear combination of the logged independent vars. $\kappa ; \mu ; \alpha$ and $\beta$ are parameters to be estimated, $M_o$ is the mass on origin, $M_d$ is the mass on destination and $D_{od}$ is the distance between origin and destination.&lt;/p&gt;
&lt;p&gt;this is in log-normal equation;&lt;/p&gt;
&lt;p&gt;$$\ln{F_{od}} = k + \mu  \ln{M_d} + \alpha  \ln{M_d} − \beta  \ln{ D_{od}}$$&lt;/p&gt;
&lt;p&gt;and Poisson equation, which should have the same result;&lt;/p&gt;
&lt;p&gt;$$F_{od} = k + \mu \ln{M_o} + \alpha  \ln{M_d} − \beta  \ln{ D_{od}}$$&lt;/p&gt;
&lt;p&gt;Let&#39;s try both&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;log-normal model&lt;/li&gt;
&lt;li&gt;Poisson model, correct definition&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The XGBOOST offers&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;objective = &amp;ldquo;reg:squarederror&amp;rdquo;&lt;/li&gt;
&lt;li&gt;objective = &amp;ldquo;count:poisson&amp;rdquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; the regressor takes a all the data from data frame as they are, so we need to apply the transformations before we build to model as I understand.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# log all the variables
fl17[&#39;log_N_Items&#39;] = np.log(fl17[&#39;N_Items&#39;])
fl18[&#39;log_N_Items&#39;] = np.log(fl18[&#39;N_Items&#39;])

# log all the variables
fl17[&#39;1*great_circle_dist&#39;] = 1*fl17[&#39;great_circle_dist&#39;]
fl18[&#39;1*great_circle_dist&#39;] = 1*fl18[&#39;great_circle_dist&#39;]

# Do we need to do the 1-Distance???

fl17[&#39;log_NUMBER_OF_PATIENTS&#39;] = np.log(fl17[&#39;NUMBER_OF_PATIENTS&#39;])
fl18[&#39;log_NUMBER_OF_PATIENTS&#39;] = np.log(fl18[&#39;NUMBER_OF_PATIENTS&#39;])

fl17[&#39;log_Density&#39;] = np.log(fl17[&#39;Density&#39;])
fl18[&#39;log_Density&#39;] = np.log(fl18[&#39;Density&#39;])

# do the same thing for the complete data

# log all the variables
cfl17[&#39;1*great_circle_dist&#39;] = 1*cfl17[&#39;great_circle_dist&#39;]
cfl18[&#39;1*great_circle_dist&#39;] = 1*cfl18[&#39;great_circle_dist&#39;]

# Do we need to do the 1-Distance???

cfl17[&#39;log_NUMBER_OF_PATIENTS&#39;] = np.log(cfl17[&#39;NUMBER_OF_PATIENTS&#39;])
cfl18[&#39;log_NUMBER_OF_PATIENTS&#39;] = np.log(cfl18[&#39;NUMBER_OF_PATIENTS&#39;])

cfl17[&#39;log_Density&#39;] = np.log(cfl17[&#39;Density&#39;])
cfl18[&#39;log_Density&#39;] = np.log(cfl18[&#39;Density&#39;])
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;fl17log = fl17.iloc[:,[77,78,79,80]]
fl18log = fl18.iloc[:,[77,78,79,80]]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;fl18log
&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;log_N_Items&lt;/th&gt;
      &lt;th&gt;1*great_circle_dist&lt;/th&gt;
      &lt;th&gt;log_NUMBER_OF_PATIENTS&lt;/th&gt;
      &lt;th&gt;log_Density&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;2.079442&lt;/td&gt;
      &lt;td&gt;16.560437&lt;/td&gt;
      &lt;td&gt;9.831723&lt;/td&gt;
      &lt;td&gt;3.602668&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;3.044522&lt;/td&gt;
      &lt;td&gt;0.653229&lt;/td&gt;
      &lt;td&gt;9.831723&lt;/td&gt;
      &lt;td&gt;3.553013&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;8.577159&lt;/td&gt;
      &lt;td&gt;0.983999&lt;/td&gt;
      &lt;td&gt;9.831723&lt;/td&gt;
      &lt;td&gt;3.370474&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;3.258097&lt;/td&gt;
      &lt;td&gt;12.816035&lt;/td&gt;
      &lt;td&gt;9.831723&lt;/td&gt;
      &lt;td&gt;3.355851&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;0.000000&lt;/td&gt;
      &lt;td&gt;20.302017&lt;/td&gt;
      &lt;td&gt;9.831723&lt;/td&gt;
      &lt;td&gt;3.428596&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;...&lt;/th&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;71729&lt;/th&gt;
      &lt;td&gt;0.000000&lt;/td&gt;
      &lt;td&gt;0.973280&lt;/td&gt;
      &lt;td&gt;6.907755&lt;/td&gt;
      &lt;td&gt;5.144711&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;71730&lt;/th&gt;
      &lt;td&gt;0.000000&lt;/td&gt;
      &lt;td&gt;1.015876&lt;/td&gt;
      &lt;td&gt;6.907755&lt;/td&gt;
      &lt;td&gt;4.824431&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;71731&lt;/th&gt;
      &lt;td&gt;0.693147&lt;/td&gt;
      &lt;td&gt;1.507605&lt;/td&gt;
      &lt;td&gt;6.907755&lt;/td&gt;
      &lt;td&gt;4.806714&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;71732&lt;/th&gt;
      &lt;td&gt;1.098612&lt;/td&gt;
      &lt;td&gt;1.739077&lt;/td&gt;
      &lt;td&gt;6.907755&lt;/td&gt;
      &lt;td&gt;4.549942&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;71733&lt;/th&gt;
      &lt;td&gt;0.693147&lt;/td&gt;
      &lt;td&gt;1.578409&lt;/td&gt;
      &lt;td&gt;6.907755&lt;/td&gt;
      &lt;td&gt;4.358013&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;71734 rows × 4 columns&lt;/p&gt;
&lt;/div&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;fl17log.hist(figsize = (10,10));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_22_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;log-normal-xgboost-sim-model&#34;&gt;Log-normal XGBoost SIM model&lt;/h2&gt;
&lt;h3 id=&#34;parameters&#34;&gt;Parameters&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;booster = &amp;lsquo;dart&amp;rsquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;XGBoost mostly combines a huge number of regression trees with a small learning rate. In this situation, trees added early are significant and trees added late are unimportant. Vinayak and Gilad-Bachrach proposed a new method to add dropout techniques from the deep neural net community to boosted trees and reported better results in some situations. Drop trees to solve the over-fitting. Trivial trees (to correct trivial errors) may be prevented. Because of the randomness introduced in the training, expect the following few differences: Training can be slower than gbtree because the random dropout prevents usage of the prediction buffer. The early stop might not be stable, due to the randomness.&lt;/p&gt;
&lt;p&gt;Follow &lt;a href=&#34;https://www.analyticsvidhya.com/blog/2016/03/complete-guide-parameter-tuning-xgboost-with-codes-python/&#34;&gt;here&lt;/a&gt; to see all the parameters.&lt;/p&gt;
&lt;p&gt;I also found &lt;a href=&#34;https://datascience-enthusiast.com/R/ML_python_R_part2.html&#34;&gt;this tutorial&lt;/a&gt; which has used the grid search from &amp;lsquo;scikit&amp;rsquo; cross validation to test several versions of the model and it&#39;s parameters. This comes very handy, as we can instantly find the best performing model in instance. Maybe &lt;a href=&#34;https://www.kaggle.com/tilii7/hyperparameter-grid-search-with-xgboost&#34;&gt;this&lt;/a&gt; can help to explain a bit more about that.&lt;/p&gt;
&lt;p&gt;Learning rate = Step size shrinkage used in update to prevents overfitting. After each boosting step, we can directly get the weights of new features, and eta shrinks the feature weights to make the boosting process more conservative.&lt;/p&gt;
&lt;p&gt;max_depth = Maximum depth of a tree. Increasing this value will make the model more complex and more likely to overfit. 0 is only accepted in lossguided growing policy when tree_method is set as hist and it indicates no limit on depth. Beware that XGBoost aggressively consumes memory when training a deep tree. Range: [0,∞] (0 is only accepted in lossguided growing policy when tree_method is set as hist)&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;h4 id=&#34;note&#34;&gt;Note&lt;/h4&gt;
&lt;p&gt;Below, you can see an example for grid search for the best model within certain parameters. It was very efficient when I was working with the log-normal model only, however, using it within the Poisson version, the model converges after third of all the runs (depends how many options you give it)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# define the training and testing dataset
X_train, y_train = fl17log.loc[:,fl17log.columns != &#39;log_N_Items&#39;], fl17log.loc[:,[&#39;log_N_Items&#39;]]
X_test, y_test = fl18log.loc[:,fl18log.columns != &#39;log_N_Items&#39;], fl18log.loc[:,[&#39;log_N_Items&#39;]]

# transfer those into matrix as recommended class by in the package
DM_train = xgb.DMatrix(data = X_train, 
                       label = y_train)
					   
DM_test =  xgb.DMatrix(data = X_test,
                       label = y_test)

# define the parameters that we want to inspect
gbm_param_grid = {
     &#39;colsample_bytree&#39;: np.linspace(0.5, 0.9), # define fairly wide range
     &#39;n_estimators&#39;:[15, 20], # try different numbers
     &#39;max_depth&#39;: [ 15, 20] # different depths
}

# define the regressor model
gbm = xgb.XGBRegressor(objective = &amp;quot;reg:squarederror&amp;quot;, booster = &#39;dart&#39;)

# define the grid search cross validation 
grid_mse = GridSearchCV(estimator = gbm, param_grid = gbm_param_grid, scoring = &#39;neg_mean_squared_error&#39;, cv = 5, verbose = 1)

# fit the models and go through all the parameters
grid_mse.fit(X_train, y_train)

# print the best results
print(&amp;quot;Best parameters found: &amp;quot;,grid_mse.best_params_)
print(&amp;quot;Lowest RMSE found: &amp;quot;, np.sqrt(np.abs(grid_mse.best_score_)))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define the training and testing dataset
X_train, y_train = fl17log.loc[:,fl17log.columns != &#39;log_N_Items&#39;], fl17log.loc[:,[&#39;log_N_Items&#39;]]
X_test, y_test = fl18log.loc[:,fl18log.columns != &#39;log_N_Items&#39;], fl18log.loc[:,[&#39;log_N_Items&#39;]]

# define the model (I already tested several version and stacked with the default setting and learning rate 0.4, the grid search is work in progress)
gbm = xgb.XGBRegressor(objective = &amp;quot;reg:squarederror&amp;quot;, learning_rate= 0.4).fit(X_train, y_train)

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;pred = gbm.predict(X_test)

print(&amp;quot;Root mean square error for test dataset: {}&amp;quot;.format(np.round(np.sqrt(mean_squared_error(np.exp(y_test), np.exp(pred))), 2)))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Root mean square error for test dataset: 949.98
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;test = pd.DataFrame({&amp;quot;prediction&amp;quot;: np.exp(pred), &amp;quot;observed&amp;quot;: np.exp(y_test[&#39;log_N_Items&#39;])})

lowess = sm.nonparametric.lowess

z = lowess(np.exp(pred).flatten(), np.exp(y_test[&#39;log_N_Items&#39;]))


test.plot(figsize = [14,8],
          x =&amp;quot;prediction&amp;quot;, y = &amp;quot;observed&amp;quot;, kind = &amp;quot;scatter&amp;quot;, color = &#39;darkred&#39;)

plt.title(&amp;quot;Extreme Gradient Boosting: Prediction Vs Test Data&amp;quot;, fontsize = 18, color = &amp;quot;darkgreen&amp;quot;)

plt.xlabel(&amp;quot;Predicted Power Output&amp;quot;, fontsize = 18) 

plt.ylabel(&amp;quot;Observed Power Output&amp;quot;, fontsize = 18)

plt.plot(z[:,0], z[:,1], color = &amp;quot;blue&amp;quot;, lw= 3)

plt.show()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_29_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;fl17[&#39;log&#39;] =  np.exp(gbm.predict(X_train))

fl18[&#39;log&#39;] =  np.exp(gbm.predict(X_test))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;poisson-xgboost-sim-model&#34;&gt;Poisson XGBoost SIM model&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create new dataset for the Poisson
# this will be logged masses, 1*dist and no change to y
fl17poi = fl17.iloc[:,[6,78,79,80]]
fl18poi = fl18.iloc[:,[6,78,79,80]]
cfl17poi = cfl17.iloc[:,[9,10,11,5]]
cfl18poi = cfl18.iloc[:,[9,10,11,5]]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;fl17poi.hist(figsize = (10,10));
cfl17poi.hist(figsize = (10,10));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_33_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_33_1.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;for-published-data-only&#34;&gt;For published data only&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define the training and testing dataset
X_train2, y_train2 = fl17poi.loc[:,fl17poi.columns != &#39;N_Items&#39;], fl17poi.loc[:,[&#39;N_Items&#39;]]
X_test2, y_test2 = fl18poi.loc[:,fl18poi.columns != &#39;N_Items&#39;], fl18poi.loc[:,[&#39;N_Items&#39;]]

# define the model
gbm2 = xgb.XGBRegressor(objective = &amp;quot;count:poisson&amp;quot;,  learning_rate= 0.4).fit(X_train2, y_train2)

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;for-complete-data-with-all-the-0-flows-in&#34;&gt;For complete data with all the 0 flows in&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define the training and testing dataset
X_train3, y_train3 = cfl17poi.loc[:,cfl17poi.columns != &#39;N_Items&#39;], cfl17poi.loc[:,[&#39;N_Items&#39;]]
X_test3, y_test3 = cfl18poi.loc[:,cfl18poi.columns != &#39;N_Items&#39;], cfl18poi.loc[:,[&#39;N_Items&#39;]]

gbm3 = xgb.XGBRegressor(objective = &amp;quot;count:poisson&amp;quot;,  learning_rate= 0.4).fit(X_train3, y_train3)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Best parameters found:  {&amp;lsquo;colsample_bytree&amp;rsquo;: 0.8, &amp;lsquo;max_depth&amp;rsquo;: 40, &amp;lsquo;n_estimators&amp;rsquo;: 30}
Lowest RMSE found:  1159.4761503247805&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;pred2 = gbm2.predict(X_test2)

print(&amp;quot;Root mean square error for test dataset: {}&amp;quot;.format(np.round(np.sqrt(mean_squared_error(y_test2, pred2)), 2)))

pred3 = gbm3.predict(X_test3)

print(&amp;quot;Root mean square error for test dataset: {}&amp;quot;.format(np.round(np.sqrt(mean_squared_error(y_test3, pred3)), 2)))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Root mean square error for test dataset: 704.17
Root mean square error for test dataset: 279.73
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;sns.regplot(np.exp(pred), np.exp(y_test[&#39;log_N_Items&#39;]), color = &#39;blue&#39;,line_kws = {&#39;color&#39;:&#39;red&#39;})
plt.title(&#39;Predicted vs actual, Log-normal XGBOOST, published data only&#39;, fontsize=20);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_40_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;sns.regplot(pred2, y_test2[&#39;N_Items&#39;], color = &#39;blue&#39;,line_kws = {&#39;color&#39;:&#39;red&#39;})
plt.title(&#39;Predicted vs actual, Poisson XGBOOST, published data only&#39;, fontsize=20);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_41_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;sns.regplot(pred3, y_test3[&#39;N_Items&#39;], color = &#39;blue&#39;,line_kws = {&#39;color&#39;:&#39;red&#39;})
plt.title(&#39;Predicted vs actual, Poisson XGBOOST, complete data&#39;, fontsize=20);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_42_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;the-poisson-seems-to-be-better-when-using-the-default-parameter-setting-and-learning-rate-04&#34;&gt;The Poisson seems to be better when using the default parameter setting and learning rate 0.4&lt;/h4&gt;
&lt;p&gt;We already have the in sample and out of sample predictions from gravity model in the data, so now we need to add the in and out of sample prediction from the XGB model&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# published
fl17[&#39;xgb&#39;] =  gbm2.predict(X_train2)

fl18[&#39;xgb&#39;] =  gbm2.predict(X_test2)

# complete
cfl17[&#39;xgb&#39;] =  gbm2.predict(X_train3)

cfl18[&#39;xgb&#39;] =  gbm2.predict(X_test3)

fl18.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;P_Code&lt;/th&gt;
      &lt;th&gt;P_Name_x&lt;/th&gt;
      &lt;th&gt;P_Postcode&lt;/th&gt;
      &lt;th&gt;D_Code_x&lt;/th&gt;
      &lt;th&gt;D_Name_x&lt;/th&gt;
      &lt;th&gt;D_Postcode&lt;/th&gt;
      &lt;th&gt;N_Items&lt;/th&gt;
      &lt;th&gt;N_EPSItems&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;loop&lt;/th&gt;
      &lt;th&gt;...&lt;/th&gt;
      &lt;th&gt;CODE&lt;/th&gt;
      &lt;th&gt;POSTCODE&lt;/th&gt;
      &lt;th&gt;NUMBER_OF_PATIENTS&lt;/th&gt;
      &lt;th&gt;predicted&lt;/th&gt;
      &lt;th&gt;log_N_Items&lt;/th&gt;
      &lt;th&gt;1*great_circle_dist&lt;/th&gt;
      &lt;th&gt;log_NUMBER_OF_PATIENTS&lt;/th&gt;
      &lt;th&gt;log_Density&lt;/th&gt;
      &lt;th&gt;log&lt;/th&gt;
      &lt;th&gt;xgb&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;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FA696&lt;/td&gt;
      &lt;td&gt;THE BULLEN HEALTHCARE GROUP LIMITED&lt;/td&gt;
      &lt;td&gt;BS32 4JT&lt;/td&gt;
      &lt;td&gt;8.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;18615&lt;/td&gt;
      &lt;td&gt;0.021203&lt;/td&gt;
      &lt;td&gt;2.079442&lt;/td&gt;
      &lt;td&gt;16.560437&lt;/td&gt;
      &lt;td&gt;9.831723&lt;/td&gt;
      &lt;td&gt;3.602668&lt;/td&gt;
      &lt;td&gt;3.172393&lt;/td&gt;
      &lt;td&gt;2.952730&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FCT35&lt;/td&gt;
      &lt;td&gt;LLOYDS PHARMACY LTD&lt;/td&gt;
      &lt;td&gt;BS20 7QA&lt;/td&gt;
      &lt;td&gt;21.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;18615&lt;/td&gt;
      &lt;td&gt;2417.864485&lt;/td&gt;
      &lt;td&gt;3.044522&lt;/td&gt;
      &lt;td&gt;0.653229&lt;/td&gt;
      &lt;td&gt;9.831723&lt;/td&gt;
      &lt;td&gt;3.553013&lt;/td&gt;
      &lt;td&gt;163.713943&lt;/td&gt;
      &lt;td&gt;861.965210&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FDD77&lt;/td&gt;
      &lt;td&gt;JOHN WARE LIMITED&lt;/td&gt;
      &lt;td&gt;BS20 6LT&lt;/td&gt;
      &lt;td&gt;5309.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;18615&lt;/td&gt;
      &lt;td&gt;2170.860226&lt;/td&gt;
      &lt;td&gt;8.577159&lt;/td&gt;
      &lt;td&gt;0.983999&lt;/td&gt;
      &lt;td&gt;9.831723&lt;/td&gt;
      &lt;td&gt;3.370474&lt;/td&gt;
      &lt;td&gt;833.031494&lt;/td&gt;
      &lt;td&gt;3730.304199&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FD125&lt;/td&gt;
      &lt;td&gt;BOOTS UK LIMITED&lt;/td&gt;
      &lt;td&gt;BS34 5UP&lt;/td&gt;
      &lt;td&gt;26.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;18615&lt;/td&gt;
      &lt;td&gt;0.390401&lt;/td&gt;
      &lt;td&gt;3.258097&lt;/td&gt;
      &lt;td&gt;12.816035&lt;/td&gt;
      &lt;td&gt;9.831723&lt;/td&gt;
      &lt;td&gt;3.355851&lt;/td&gt;
      &lt;td&gt;6.255939&lt;/td&gt;
      &lt;td&gt;5.463578&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;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FEA59&lt;/td&gt;
      &lt;td&gt;LLOYDS PHARMACY LTD&lt;/td&gt;
      &lt;td&gt;BS16 7AE&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;18615&lt;/td&gt;
      &lt;td&gt;0.001570&lt;/td&gt;
      &lt;td&gt;0.000000&lt;/td&gt;
      &lt;td&gt;20.302017&lt;/td&gt;
      &lt;td&gt;9.831723&lt;/td&gt;
      &lt;td&gt;3.428596&lt;/td&gt;
      &lt;td&gt;3.263482&lt;/td&gt;
      &lt;td&gt;1.575869&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;5 rows × 83 columns&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;the-accuracy-measure&#34;&gt;The accuracy measure&lt;/h2&gt;
&lt;p&gt;Lets organize this into a table;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&#34;center&#34;&gt;&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;R2 Gravity SIM&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;R2 XGBOOST SIM&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;RMSE Gravity SIM&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;RMSE XGBOOST SIM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&#34;center&#34;&gt;Models based on published data&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGIr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXIr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGIrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXIrs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&#34;center&#34;&gt;&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGOr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXOr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGOrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXOrs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&#34;center&#34;&gt;Models based on complete data&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGIr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXIr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGIrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXIrs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&#34;center&#34;&gt;&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGOr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXOr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGOrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXOrs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define R2 function
def get_r2_numpy_corrcoef(x, y):
    return np.corrcoef(x, y)[0, 1]**2
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define RMSE function
def rmse(predictions, targets):
    return np.sqrt(((predictions - targets) ** 2).mean())
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;other-evaluations&#34;&gt;Other evaluations?&lt;/h2&gt;
&lt;p&gt;Some is available at &lt;a href=&#34;https://github.com/calebrob6/migration-lib/blob/master/EvaluationMethods.py&#34;&gt;Robinsons github&lt;/a&gt;, but requires matrices.&lt;/p&gt;
&lt;p&gt;See to definition of the Common Part of Commuters measure. &lt;a href=&#34;https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0045985&#34;&gt;Source article&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def cpc(actual, predicted):
    
    # reshape the variables 
    predicted = np.reshape(np.array(predicted), (-1, 1))
    actual = np.reshape(np.array(actual),(-1, 1))
    # stack them together
    YYhat = np.hstack([actual, predicted])
    # define sum of the minimym of the predicted var
    NCC = np.sum(np.min(YYhat, axis=1)) # , axis=1
    # number of observations
    N = len(actual)
    # sum of all the actual numbers (reshaped)
    NCY = np.sum(actual)
    # sum of the predicted numbers (reshaped)
    NCYhat = np.sum(predicted)
    
    numerator = (N * NCC)
    denominator = (NCY + NCYhat)
    
    return numerator/denominator
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;$$ \frac{ N_i * \sum \min(ij) }{ \sum_i + \sum_j } $$&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def sorensen(actual, predicted):
    # reshape the variables
    predicted = np.reshape(np.array(predicted), (-1, 1))
    actual = np.reshape(np.array(actual),(-1, 1))
    # define length of the variables
    N =  len(actual)
    # stack them together in array
    YYhat = np.hstack([actual, predicted])
    # define 2 times minimum from both
    num = 2.0 * np.min(YYhat, axis=1)
    # define the differnece
    den =  predicted + actual
           
    return (1.0 / N) * (np.sum(num.reshape((-1, 1)) / den.reshape((-1, 1))))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;$$ \frac{1}{N} * \frac{\sum{2 * \min(i,j)} }{j-i}  $$&lt;/p&gt;
&lt;h1 id=&#34;lets-do-the-result-tables-again-with-those-measures&#34;&gt;Let&#39;s do the result tables again with those measures&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&#34;right&#34;&gt;&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;R2 Gravity SIM&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;R2 XGBOOST SIM&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;RMSE Gravity SIM&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;RMSE XGBOOST SIM&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;CPC Gravity SIM&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;CPC XGBOOST SIM&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;SORENSEN I Gravity&lt;/th&gt;
&lt;th align=&#34;center&#34;&gt;SORENSEN I XGBOOST&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&#34;right&#34;&gt;Models based on published data&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGIr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXIr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGIrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXIrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGIcpc&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXIcpc&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGIsn&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXIsn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&#34;right&#34;&gt;&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGOr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXOr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGOrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXOrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGOcpc&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXOcpc&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PGOsn&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;PXOsn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&#34;right&#34;&gt;Models based on complete data&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGIr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXIr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGIrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXIrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGIcpc&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXIcpc&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGIsn&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXIsn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&#34;right&#34;&gt;&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGOr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXOr2&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGOrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXOrs&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGOcpc&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXOcpc&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CGOsn&lt;/td&gt;
&lt;td align=&#34;center&#34;&gt;CXOsn&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# R2
# published data
PGIr2 = round(get_r2_numpy_corrcoef(fl17[&#39;N_Items&#39;], fl17[&#39;yhat&#39;]),3)
PGOr2 = round(get_r2_numpy_corrcoef(fl18[&#39;N_Items&#39;], fl18[&#39;predicted&#39;]),3)

PXIr2 = round(get_r2_numpy_corrcoef(fl17[&#39;N_Items&#39;], fl17[&#39;xgb&#39;]),3)
PXOr2 = round(get_r2_numpy_corrcoef(fl18[&#39;N_Items&#39;], fl18[&#39;xgb&#39;]),3)

# complete data
CGIr2 = round(get_r2_numpy_corrcoef(cfl17[&#39;N_Items&#39;], cfl17[&#39;yhat&#39;]),3)
CGOr2 = round(get_r2_numpy_corrcoef(cfl18[&#39;N_Items&#39;], cfl18[&#39;predicted&#39;]),3)

CXIr2 = round(get_r2_numpy_corrcoef(cfl17[&#39;N_Items&#39;], cfl17[&#39;xgb&#39;]),3)
CXOr2 = round(get_r2_numpy_corrcoef(cfl18[&#39;N_Items&#39;], cfl18[&#39;xgb&#39;]),3)

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# RMSE

PGIrs = round(rmse(fl17[&#39;N_Items&#39;], fl17[&#39;yhat&#39;]),1)
PGOrs = round(rmse(fl18[&#39;N_Items&#39;], fl18[&#39;predicted&#39;]),1)

PXIrs = round(rmse(fl17[&#39;N_Items&#39;], fl17[&#39;xgb&#39;]),1)
PXOrs = round(rmse(fl18[&#39;N_Items&#39;], fl18[&#39;xgb&#39;]),1)

CGIrs = round(rmse(cfl17[&#39;N_Items&#39;], cfl17[&#39;yhat&#39;]),1)
CGOrs = round(rmse(cfl18[&#39;N_Items&#39;], cfl18[&#39;predicted&#39;]),1)

CXIrs = round(rmse(cfl17[&#39;N_Items&#39;], cfl17[&#39;xgb&#39;]),1)
CXOrs = round(rmse(cfl18[&#39;N_Items&#39;], cfl18[&#39;xgb&#39;]),1)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# CPC

PGIcpc = round(cpc(fl17[&#39;N_Items&#39;], fl17[&#39;yhat&#39;]),1)
PGOcpc = round(cpc(fl18[&#39;N_Items&#39;], fl18[&#39;predicted&#39;]),1)

PXIcpc = round(cpc(fl17[&#39;N_Items&#39;], fl17[&#39;xgb&#39;]),1)
PXOcpc = round(cpc(fl18[&#39;N_Items&#39;], fl18[&#39;xgb&#39;]),1)

CGIcpc = round(cpc(cfl17[&#39;N_Items&#39;], cfl17[&#39;yhat&#39;]),1)
CGOcpc = round(cpc(cfl18[&#39;N_Items&#39;], cfl18[&#39;predicted&#39;]),1)

CXIcpc = round(cpc(cfl17[&#39;N_Items&#39;], cfl17[&#39;xgb&#39;]),1)
CXOcpc = round(cpc(cfl18[&#39;N_Items&#39;], cfl18[&#39;xgb&#39;]),1)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# Sorensen 

PGIsn = round(sorensen(fl17[&#39;N_Items&#39;], fl17[&#39;yhat&#39;]),3)
PGOsn = round(sorensen(fl18[&#39;N_Items&#39;], fl18[&#39;predicted&#39;]),3)

PXIsn = round(sorensen(fl17[&#39;N_Items&#39;], fl17[&#39;xgb&#39;]),3)
PXOsn = round(sorensen(fl18[&#39;N_Items&#39;], fl18[&#39;xgb&#39;]),3)

CGIsn = round(sorensen(cfl17[&#39;N_Items&#39;], cfl17[&#39;yhat&#39;]),3)
CGOsn = round(sorensen(cfl18[&#39;N_Items&#39;], cfl18[&#39;predicted&#39;]),3)

CXIsn = round(sorensen(cfl17[&#39;N_Items&#39;], cfl17[&#39;xgb&#39;]),3)
CXOsn = round(sorensen(cfl18[&#39;N_Items&#39;], cfl18[&#39;xgb&#39;]),3)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# everything for the log-normal model
LXIr2 = round(get_r2_numpy_corrcoef(fl17[&#39;N_Items&#39;], fl17[&#39;log&#39;]),3)
LXOr2 = round(get_r2_numpy_corrcoef(fl18[&#39;N_Items&#39;], fl18[&#39;log&#39;]),3)

LXIrs = round(rmse(fl17[&#39;N_Items&#39;], fl17[&#39;log&#39;]),1)
LXOrs = round(rmse(fl18[&#39;N_Items&#39;], fl18[&#39;log&#39;]),1)

LXIcpc = round(cpc(fl17[&#39;N_Items&#39;], fl17[&#39;log&#39;]),1)
LXOcpc = round(cpc(fl18[&#39;N_Items&#39;], fl18[&#39;log&#39;]),1)

LXIsn = round(sorensen(fl17[&#39;N_Items&#39;], fl17[&#39;log&#39;]),3)
LXOsn = round(sorensen(fl18[&#39;N_Items&#39;], fl18[&#39;log&#39;]),3)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;model_name = [&#39;In-sample&#39;,&#39;Out-sample&#39;]
mG = [PGIr2, PGOr2]
mX = [PXIr2, PXOr2]
mF = [PGIrs, PGOrs]
mH = [PXIrs, PXOrs]
mD1 = [PGIcpc, PGOcpc]
mD2 = [PXIcpc, PXOcpc]
mK1 = [PGIsn, PGOsn]
mK2 = [PXIsn, PXOsn]

cols = {&#39;Poisson models based on published data&#39;: model_name,&#39;R2 Gravity SIM&#39;: mG, &#39;R2 XGBOOST SIM&#39;: mX, &#39;RMSE Gravity SIM&#39;: mF, &#39;RMSE XGBOOST SIM&#39;: mH, &#39;CPC Gravity SIM&#39;: mD1,
      &#39;CPC XGBOOST SIM&#39;: mD2,  &#39;SORENSEN I Gravity&#39;: mK1, &#39;SORENSEN I XGBOOST&#39;: mK2}

table_published = pd.DataFrame(cols).set_index(&#39;Poisson models based on published data&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;model_name = [&#39;In-sample&#39;,&#39;Out-sample&#39;]
G = [CGIr2, CGOr2]
X = [CXIr2, CXOr2]
mF = [CGIrs, CGOrs]
mH = [CXIrs, CXOrs]
mD1 = [CGIcpc, CGOcpc]
mD2 = [CXIcpc, CXOcpc]
mK1 = [CGIsn, CGOsn]
mK2 = [CXIsn, CXOsn]

cols = {&#39;Poisson models based on complete data&#39;: model_name,&#39;R2 Gravity SIM&#39;: G, &#39;R2 XGBOOST SIM&#39;: X, &#39;RMSE Gravity SIM&#39;: mF, &#39;RMSE XGBOOST SIM&#39;: mH,  &#39;CPC Gravity SIM&#39;: mD1,
      &#39;CPC XGBOOST SIM&#39;: mD2,  &#39;SORENSEN I Gravity&#39;: mK1, &#39;SORENSEN I XGBOOST&#39;: mK2}

table_complete = pd.DataFrame(cols).set_index(&#39;Poisson models based on complete data&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;NA = &#39;NA&#39;
model_name = [&#39;In-sample&#39;,&#39;Out-sample&#39;]
mG = [NA, NA]
mX = [LXIr2, LXOr2]
mF = [NA, NA]
mH = [LXIrs, LXOrs]
mD1 = [NA, NA]
mD2 = [LXIcpc, LXOcpc]
mK1 = [NA, NA]
mK2 = [LXIsn, LXOsn]

cols = {&#39;Log-normal models based on published data&#39;: model_name,&#39;R2 Gravity SIM&#39;: mG, &#39;R2 XGBOOST SIM&#39;: mX, &#39;RMSE Gravity SIM&#39;: mF, &#39;RMSE XGBOOST SIM&#39;: mH, &#39;CPC Gravity SIM&#39;: mD1,
      &#39;CPC XGBOOST SIM&#39;: mD2,  &#39;SORENSEN I Gravity&#39;: mK1, &#39;SORENSEN I XGBOOST&#39;: mK2}

table_log = pd.DataFrame(cols).set_index(&#39;Log-normal models based on published data&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;table_log
&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;R2 Gravity SIM&lt;/th&gt;
      &lt;th&gt;R2 XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;RMSE Gravity SIM&lt;/th&gt;
      &lt;th&gt;RMSE XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;CPC Gravity SIM&lt;/th&gt;
      &lt;th&gt;CPC XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;SORENSEN I Gravity&lt;/th&gt;
      &lt;th&gt;SORENSEN I XGBOOST&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Log-normal models based on published data&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;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;In-sample&lt;/th&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;0.560&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;809.9&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;25504.1&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;0.686&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Out-sample&lt;/th&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;0.381&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;950.0&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;21690.5&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;0.620&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;# models based on published data only
table_published
&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;R2 Gravity SIM&lt;/th&gt;
      &lt;th&gt;R2 XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;RMSE Gravity SIM&lt;/th&gt;
      &lt;th&gt;RMSE XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;CPC Gravity SIM&lt;/th&gt;
      &lt;th&gt;CPC XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;SORENSEN I Gravity&lt;/th&gt;
      &lt;th&gt;SORENSEN I XGBOOST&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Poisson models based on published data&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;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;In-sample&lt;/th&gt;
      &lt;td&gt;0.321&lt;/td&gt;
      &lt;td&gt;0.736&lt;/td&gt;
      &lt;td&gt;976.2&lt;/td&gt;
      &lt;td&gt;611.1&lt;/td&gt;
      &lt;td&gt;17813.1&lt;/td&gt;
      &lt;td&gt;27736.8&lt;/td&gt;
      &lt;td&gt;0.318&lt;/td&gt;
      &lt;td&gt;0.513&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Out-sample&lt;/th&gt;
      &lt;td&gt;0.330&lt;/td&gt;
      &lt;td&gt;0.653&lt;/td&gt;
      &lt;td&gt;981.4&lt;/td&gt;
      &lt;td&gt;704.2&lt;/td&gt;
      &lt;td&gt;17207.5&lt;/td&gt;
      &lt;td&gt;24790.0&lt;/td&gt;
      &lt;td&gt;0.314&lt;/td&gt;
      &lt;td&gt;0.490&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;# models based on complete data with all the zero flows included
table_complete
&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;R2 Gravity SIM&lt;/th&gt;
      &lt;th&gt;R2 XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;RMSE Gravity SIM&lt;/th&gt;
      &lt;th&gt;RMSE XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;CPC Gravity SIM&lt;/th&gt;
      &lt;th&gt;CPC XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;SORENSEN I Gravity&lt;/th&gt;
      &lt;th&gt;SORENSEN I XGBOOST&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Poisson models based on complete data&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;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;In-sample&lt;/th&gt;
      &lt;td&gt;0.287&lt;/td&gt;
      &lt;td&gt;0.691&lt;/td&gt;
      &lt;td&gt;380.6&lt;/td&gt;
      &lt;td&gt;249.9&lt;/td&gt;
      &lt;td&gt;106667.5&lt;/td&gt;
      &lt;td&gt;168990.5&lt;/td&gt;
      &lt;td&gt;0.045&lt;/td&gt;
      &lt;td&gt;0.070&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Out-sample&lt;/th&gt;
      &lt;td&gt;0.297&lt;/td&gt;
      &lt;td&gt;0.605&lt;/td&gt;
      &lt;td&gt;385.3&lt;/td&gt;
      &lt;td&gt;288.8&lt;/td&gt;
      &lt;td&gt;100976.7&lt;/td&gt;
      &lt;td&gt;145788.9&lt;/td&gt;
      &lt;td&gt;0.046&lt;/td&gt;
      &lt;td&gt;0.068&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;x = table_log.append(table_published)
x = x.append(table_complete)
x[&#39;model_type&#39;] = [&#39;log-normal&#39;, &#39;log-normal&#39;, &#39;Poisson&#39;, &#39;Poisson&#39;, &#39;Poisson&#39;, &#39;Poisson&#39;]
x[&#39;data_type&#39;] = [&#39;published_only&#39;, &#39;published_only&#39;, &#39;published_only&#39;, &#39;published_only&#39;, &#39;complete&#39;, &#39;complete&#39;]
x[&#39;CV&#39;] = [&#39;In-sample&#39;, &#39;Out-sample&#39;,&#39;In-sample&#39;, &#39;Out-sample&#39;,&#39;In-sample&#39;, &#39;Out-sample&#39;]
x = x.set_index([&#39;model_type&#39;, &#39;data_type&#39;])
x
&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;&lt;/th&gt;
      &lt;th&gt;R2 Gravity SIM&lt;/th&gt;
      &lt;th&gt;R2 XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;RMSE Gravity SIM&lt;/th&gt;
      &lt;th&gt;RMSE XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;CPC Gravity SIM&lt;/th&gt;
      &lt;th&gt;CPC XGBOOST SIM&lt;/th&gt;
      &lt;th&gt;SORENSEN I Gravity&lt;/th&gt;
      &lt;th&gt;SORENSEN I XGBOOST&lt;/th&gt;
      &lt;th&gt;CV&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;model_type&lt;/th&gt;
      &lt;th&gt;data_type&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;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 rowspan=&#34;2&#34; valign=&#34;top&#34;&gt;log-normal&lt;/th&gt;
      &lt;th&gt;published_only&lt;/th&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;0.560&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;809.9&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;25504.1&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;0.686&lt;/td&gt;
      &lt;td&gt;In-sample&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;published_only&lt;/th&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;0.381&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;950.0&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;21690.5&lt;/td&gt;
      &lt;td&gt;NA&lt;/td&gt;
      &lt;td&gt;0.620&lt;/td&gt;
      &lt;td&gt;Out-sample&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th rowspan=&#34;4&#34; valign=&#34;top&#34;&gt;Poisson&lt;/th&gt;
      &lt;th&gt;published_only&lt;/th&gt;
      &lt;td&gt;0.321&lt;/td&gt;
      &lt;td&gt;0.736&lt;/td&gt;
      &lt;td&gt;976.2&lt;/td&gt;
      &lt;td&gt;611.1&lt;/td&gt;
      &lt;td&gt;17813.1&lt;/td&gt;
      &lt;td&gt;27736.8&lt;/td&gt;
      &lt;td&gt;0.318&lt;/td&gt;
      &lt;td&gt;0.513&lt;/td&gt;
      &lt;td&gt;In-sample&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;published_only&lt;/th&gt;
      &lt;td&gt;0.33&lt;/td&gt;
      &lt;td&gt;0.653&lt;/td&gt;
      &lt;td&gt;981.4&lt;/td&gt;
      &lt;td&gt;704.2&lt;/td&gt;
      &lt;td&gt;17207.5&lt;/td&gt;
      &lt;td&gt;24790.0&lt;/td&gt;
      &lt;td&gt;0.314&lt;/td&gt;
      &lt;td&gt;0.490&lt;/td&gt;
      &lt;td&gt;Out-sample&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;complete&lt;/th&gt;
      &lt;td&gt;0.287&lt;/td&gt;
      &lt;td&gt;0.691&lt;/td&gt;
      &lt;td&gt;380.6&lt;/td&gt;
      &lt;td&gt;249.9&lt;/td&gt;
      &lt;td&gt;106668&lt;/td&gt;
      &lt;td&gt;168990.5&lt;/td&gt;
      &lt;td&gt;0.045&lt;/td&gt;
      &lt;td&gt;0.070&lt;/td&gt;
      &lt;td&gt;In-sample&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;complete&lt;/th&gt;
      &lt;td&gt;0.297&lt;/td&gt;
      &lt;td&gt;0.605&lt;/td&gt;
      &lt;td&gt;385.3&lt;/td&gt;
      &lt;td&gt;288.8&lt;/td&gt;
      &lt;td&gt;100977&lt;/td&gt;
      &lt;td&gt;145788.9&lt;/td&gt;
      &lt;td&gt;0.046&lt;/td&gt;
      &lt;td&gt;0.068&lt;/td&gt;
      &lt;td&gt;Out-sample&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;x.iloc[[0,2,4],:][&#39;R2 XGBOOST SIM&#39;].plot() 
x.iloc[[1,3,5],:][&#39;R2 XGBOOST SIM&#39;].plot()
plt.title(&#39;R2 XGBOOST&#39;, fontsize=20)
plt.legend([&#39;In-sample&#39;, &#39;Out-sample&#39;])
plt.xlabel(&#39; &#39;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_71_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;x.iloc[[0,2,4],:][&#39;RMSE XGBOOST SIM&#39;].plot()
x.iloc[[1,3,5],:][&#39;RMSE XGBOOST SIM&#39;].plot()
plt.title(&#39;RMSE  XGBOOST&#39;, fontsize=20)
plt.legend([&#39;In-sample&#39;, &#39;Out-sample&#39;])
plt.xlabel(&#39; &#39;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_72_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;x.iloc[[0,2,4],:][&#39;CPC XGBOOST SIM&#39;].plot()
x.iloc[[1,3,5],:][&#39;CPC XGBOOST SIM&#39;].plot()
plt.title(&#39;CPC XGBOOST&#39;, fontsize=20)
plt.legend([&#39;In-sample&#39;, &#39;Out-sample&#39;])
plt.xlabel(&#39; &#39;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_73_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;x.iloc[[0,2,4],:][&#39;SORENSEN I XGBOOST&#39;].plot()
x.iloc[[1,3,5],:][&#39;SORENSEN I XGBOOST&#39;].plot()
plt.title(&#39;SORENSEN XGBOOST&#39;, fontsize=20)
plt.legend([&#39;In-sample&#39;, &#39;Out-sample&#39;])
plt.xlabel(&#39; &#39;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./xgboost2/index_74_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Sorensen similarity Index is bounded between values of 0 and 1 with values closer to 1 indicating a better model fit.&lt;/p&gt;
&lt;p&gt;How come that the complete dataset has exceptionally low index, because of the zero issue?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;cfl17.loc[:,[&#39;N_Items&#39;, &#39;xgb&#39;]]
&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;N_Items&lt;/th&gt;
      &lt;th&gt;xgb&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;130.0&lt;/td&gt;
      &lt;td&gt;11.200489&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;39.0&lt;/td&gt;
      &lt;td&gt;7.248779&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;12.0&lt;/td&gt;
      &lt;td&gt;3.174812&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;661.0&lt;/td&gt;
      &lt;td&gt;861.965210&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;4058.0&lt;/td&gt;
      &lt;td&gt;3730.304199&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;...&lt;/th&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;543739&lt;/th&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;8.880725&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;543740&lt;/th&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;5.127942&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;543741&lt;/th&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;5.786361&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;543742&lt;/th&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;7.492306&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;543743&lt;/th&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;18.535973&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;543744 rows × 2 columns&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Prob it is caused by the excessive zeros, we would probably need to introduce an ensemble model to deal with it.&lt;/p&gt;
&lt;p&gt;Some other is available from &lt;a href=&#34;https://github.com/pysal/spint/blob/master/spint/utils.py&#34;&gt;Tylor Oshan&lt;/a&gt;
He has CPC as well as Sorensen index&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Comparing the graph structures of the patients flow</title>
      <link>https://lenkastatham.com/post/graph_nhs_compar.html</link>
      <pubDate>Thu, 26 Mar 2020 00:00:00 +0000</pubDate>
      <guid>https://lenkastatham.com/post/graph_nhs_compar.html</guid>
      <description>&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# we will first import all necessary libraries
import pandas as pd
import os
import numpy as np
import glob
from datetime import datetime
import geopandas as gpd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from shapely.geometry import Point, LineString
#from mpl_toolkits.basemap import Basemap
import osmnx as ox
import networkx as nx
import matplotlib as mpl
from networkx.algorithms import bipartite as bi
import statsmodels.api as sm
from scipy import stats
from statsmodels.formula.api import glm
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;wgs84 = {&#39;init&#39;:&#39;EPSG:4326&#39;}
bng = {&#39;init&#39;:&#39;EPSG:27700&#39;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;prescription-flow-model-validation&#34;&gt;Prescription flow model validation&lt;/h1&gt;
&lt;h3 id=&#34;using-graphs-to-detect-changes-in-spatial-structure-of-the-prescription-flow&#34;&gt;Using graphs to detect changes in spatial structure of the prescription flow&lt;/h3&gt;
&lt;p&gt;Using a graphs to look at the properties of the flow network, we need to first think about what kind of graph the network actually is and what kind of connections it has.&lt;/p&gt;
&lt;p&gt;Based on the number of edges between two points we recognize&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Simple graph - Undirected graph with no loops and one edge between two points&lt;/li&gt;
&lt;li&gt;Multiple graph - Undirected graph with no loops&lt;/li&gt;
&lt;li&gt;Pseudograph - Graph with loops&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Based on the type of the link we recognise;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Undirected graph - the flow does not have a direction&lt;/li&gt;
&lt;li&gt;Directed graph - the flow have a direction, flow from point A to point B&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We also recognize;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Incomplete graph - graph in which at least one set of nodes is missing connection&lt;/li&gt;
&lt;li&gt;Complete graph - graph in which all the nodes are connected to all the other nodes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lastly, we can also recognize very special case of a graph called Bipartite.
What is Bipartiv graph?
Recently, “bipartivity” has been proposed as an important topological characteristic of complex networks. A network (graph) G = (V,E) is called bipartite if its vertex set V can be partitioned into two subsets V1 and V2 such that all edges have one endpoint in V1 and the other in V2.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The GP and pharmacy flow are in nature Directed Incomplete pseudograph. that is because the flow goes from GP to pharmacy, always one direction, it does not contain the flows between all the pairs and also contains some loops; Gp can also dispense small amount odf prescriptions.
Nevertheles, as the loops are very small proportoin of the data and we can benefit from the graph as a bipartite, I have attached dummy GPs into a pharmacy points so we can treat the loops as normal flows.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;NOTE: what is the implication of this? We are loosing the information about connection of dome flows to origin, however, we are gaining an information on the process which reminds zero-inflated or hurdle process;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Step 1 - patient come to GP but leaves without a prescriprion&lt;/li&gt;
&lt;li&gt;Step 2 - patient come to GP get the prescription virtually and recieve the drug from GP directly&lt;/li&gt;
&lt;li&gt;Step 3 - patient come to GP, get prescription, go to nearest pharmacy to get the drug&lt;/li&gt;
&lt;li&gt;Step 4 - patient come to GP, get the prescription and go to pharmacy close to shopping opportunity to get the drug&lt;/li&gt;
&lt;li&gt;Step 5 - patient come to GP, get the prescription and go to other pharmacy to get the drug&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;how-do-we-represent-the-graph-in-networkx&#34;&gt;How do we represent the graph in Networkx?&lt;/h3&gt;
&lt;p&gt;Here are types of graphs in the networkx package&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Networkx Class&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Self-loops allowed&lt;/th&gt;
&lt;th&gt;Parallel edges allowed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Graph&lt;/td&gt;
&lt;td&gt;undirected&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DiGraph&lt;/td&gt;
&lt;td&gt;directed&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MultiGraph&lt;/td&gt;
&lt;td&gt;undirected&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MultiDiGraph&lt;/td&gt;
&lt;td&gt;directed&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We know our graph is directed so &lt;strong&gt;Digraph&lt;/strong&gt; should serve the purpose.
However, the data consist from monthly prescription flow so for most of the nodes we can find 12 edges throughout the year, so we need to work with &lt;strong&gt;MultiDiGraph&lt;/strong&gt; to make sure we can account for all of those.&lt;/p&gt;
&lt;h2 id=&#34;1-load-the-data&#34;&gt;1. Load the data&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# load the output of the gravity models, the flow data
# set the path
file = r&#39;./../../data/NHS/working/predicted_flow_17.csv&#39;
# read the file
flow17 = pd.read_csv(file, index_col=None)
# plot the top of the table
flow17.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;P_Code&lt;/th&gt;
      &lt;th&gt;P_Name_x&lt;/th&gt;
      &lt;th&gt;P_Postcode&lt;/th&gt;
      &lt;th&gt;D_Code_x&lt;/th&gt;
      &lt;th&gt;D_Name_x&lt;/th&gt;
      &lt;th&gt;D_Postcode&lt;/th&gt;
      &lt;th&gt;N_Items&lt;/th&gt;
      &lt;th&gt;N_EPSItems&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;loop&lt;/th&gt;
      &lt;th&gt;...&lt;/th&gt;
      &lt;th&gt;D_Left_y&lt;/th&gt;
      &lt;th&gt;D_X_y&lt;/th&gt;
      &lt;th&gt;D_Y_y&lt;/th&gt;
      &lt;th&gt;D_E_y&lt;/th&gt;
      &lt;th&gt;D_N_y&lt;/th&gt;
      &lt;th&gt;D_Setting_y&lt;/th&gt;
      &lt;th&gt;residents&lt;/th&gt;
      &lt;th&gt;Density&lt;/th&gt;
      &lt;th&gt;predict_items&lt;/th&gt;
      &lt;th&gt;predict_items_doubly&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;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FA251&lt;/td&gt;
      &lt;td&gt;HORIZON PHARMACY LTD&lt;/td&gt;
      &lt;td&gt;BS34 6AS&lt;/td&gt;
      &lt;td&gt;130.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;20180115.0&lt;/td&gt;
      &lt;td&gt;-2.562191&lt;/td&gt;
      &lt;td&gt;51.539195&lt;/td&gt;
      &lt;td&gt;361105.872423&lt;/td&gt;
      &lt;td&gt;182404.906640&lt;/td&gt;
      &lt;td&gt;50&lt;/td&gt;
      &lt;td&gt;456.517857&lt;/td&gt;
      &lt;td&gt;36.592857&lt;/td&gt;
      &lt;td&gt;29.316590&lt;/td&gt;
      &lt;td&gt;10.581379&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FA613&lt;/td&gt;
      &lt;td&gt;DOWNHAM FB LTD&lt;/td&gt;
      &lt;td&gt;BS7 9JT&lt;/td&gt;
      &lt;td&gt;39.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;-2.582219&lt;/td&gt;
      &lt;td&gt;51.480445&lt;/td&gt;
      &lt;td&gt;359664.895464&lt;/td&gt;
      &lt;td&gt;175881.865127&lt;/td&gt;
      &lt;td&gt;50&lt;/td&gt;
      &lt;td&gt;249.144737&lt;/td&gt;
      &lt;td&gt;54.493421&lt;/td&gt;
      &lt;td&gt;29.473430&lt;/td&gt;
      &lt;td&gt;2.853510&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FCL91&lt;/td&gt;
      &lt;td&gt;MYGLOBE LTD&lt;/td&gt;
      &lt;td&gt;BS15 4ND&lt;/td&gt;
      &lt;td&gt;12.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;-2.478870&lt;/td&gt;
      &lt;td&gt;51.459944&lt;/td&gt;
      &lt;td&gt;366826.940029&lt;/td&gt;
      &lt;td&gt;173549.785593&lt;/td&gt;
      &lt;td&gt;50&lt;/td&gt;
      &lt;td&gt;249.128205&lt;/td&gt;
      &lt;td&gt;30.569231&lt;/td&gt;
      &lt;td&gt;24.155556&lt;/td&gt;
      &lt;td&gt;2.061345&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;L81004&lt;/td&gt;
      &lt;td&gt;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FCT35&lt;/td&gt;
      &lt;td&gt;LLOYDS PHARMACY LTD&lt;/td&gt;
      &lt;td&gt;BS20 7QA&lt;/td&gt;
      &lt;td&gt;661.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;20180131.0&lt;/td&gt;
      &lt;td&gt;-2.758272&lt;/td&gt;
      &lt;td&gt;51.484474&lt;/td&gt;
      &lt;td&gt;347443.775309&lt;/td&gt;
      &lt;td&gt;176441.826480&lt;/td&gt;
      &lt;td&gt;50&lt;/td&gt;
      &lt;td&gt;369.081633&lt;/td&gt;
      &lt;td&gt;34.918367&lt;/td&gt;
      &lt;td&gt;1648.893267&lt;/td&gt;
      &lt;td&gt;1078.144522&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;PORTISHEAD MEDICAL GROUP&lt;/td&gt;
      &lt;td&gt;BS20 6AQ&lt;/td&gt;
      &lt;td&gt;FDD77&lt;/td&gt;
      &lt;td&gt;JOHN WARE LIMITED&lt;/td&gt;
      &lt;td&gt;BS20 6LT&lt;/td&gt;
      &lt;td&gt;4058.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;False&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;-2.781349&lt;/td&gt;
      &lt;td&gt;51.481409&lt;/td&gt;
      &lt;td&gt;345837.744901&lt;/td&gt;
      &lt;td&gt;176117.839266&lt;/td&gt;
      &lt;td&gt;50&lt;/td&gt;
      &lt;td&gt;391.076923&lt;/td&gt;
      &lt;td&gt;29.092308&lt;/td&gt;
      &lt;td&gt;1194.123265&lt;/td&gt;
      &lt;td&gt;3570.014826&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;5 rows × 72 columns&lt;/p&gt;
&lt;/div&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create a lists of the unique codes so we can subset the GP and PH point data
list_flow_GP = list(flow17[&#39;P_Code&#39;].unique())
list_flow_PH = list(flow17[&#39;D_Code_new&#39;].unique())
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# get the pharmacies in
fl = &#39;./../../data/NHS/edispensary/PH_joined_daytimepop.geojson&#39;
PH = gpd.read_file(fl)
PH = PH.to_crs(wgs84)
PH = PH[PH[&#39;D_Code_new&#39;].isin(list_flow_PH)]
PH.plot();
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;C:\Users\tk18583\AppData\Local\Continuum\anaconda3\envs\geonet\lib\site-packages\pyproj\crs\crs.py:53: FutureWarning:

&#39;+init=&amp;lt;authority&amp;gt;:&amp;lt;code&amp;gt;&#39; syntax is deprecated. &#39;&amp;lt;authority&amp;gt;:&amp;lt;code&amp;gt;&#39; is the preferred initialization method. When making the change, be mindful of axis order changes: https://pyproj4.github.io/pyproj/stable/gotchas.html#axis-order-changes-in-proj-6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./graph_nhs_compar/index_8_1.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# get the GP&#39;s points in
GP = gpd.read_file(&amp;quot;./../../data/NHS/epraccur/GP_AVON.geojson&amp;quot;)
GP = GP[[&#39;P_Code&#39;,&#39;P_Name&#39;, &#39;full_address&#39;, &#39;P_Postcode&#39;, &#39;Open_date&#39;, 
                                         &#39;Close_date&#39;, &#39;Status_Code&#39;, &#39;Sub_type&#39;, &#39;Join&#39;, &#39;left&#39;, &#39;Setting&#39;, &#39;X&#39;, &#39;Y&#39;, &#39;geometry&#39;]]
GP = GP.rename(columns={&#39;P_Postcode&#39;:&#39;P_Postcode2&#39;,&#39;Open_date&#39;: &#39;P_Open_date&#39;, &#39;Close_date&#39;:&#39;P_Close_date&#39;,&#39;full_address&#39;:&#39;P_full_address&#39; ,&#39;Status_Code&#39;:&#39;P_Status_Code&#39;, &#39;Sub_type&#39;:&#39;P_Sub_type&#39;, &#39;Join&#39;:&#39;P_Join&#39;, &#39;left&#39;:&#39;P_Left&#39;, &#39;Setting&#39;:&#39;P_Setting&#39;, &#39;X&#39;:&#39;P_X&#39;, &#39;Y&#39;:&#39;P_Y&#39;}, errors=&amp;quot;raise&amp;quot;)
GP = GP.to_crs(wgs84)
GP = GP[GP[&#39;P_Code&#39;].isin(list_flow_GP)]
GP.plot();
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;C:\Users\tk18583\AppData\Local\Continuum\anaconda3\envs\geonet\lib\site-packages\pyproj\crs\crs.py:53: FutureWarning:

&#39;+init=&amp;lt;authority&amp;gt;:&amp;lt;code&amp;gt;&#39; syntax is deprecated. &#39;&amp;lt;authority&amp;gt;:&amp;lt;code&amp;gt;&#39; is the preferred initialization method. When making the change, be mindful of axis order changes: https://pyproj4.github.io/pyproj/stable/gotchas.html#axis-order-changes-in-proj-6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./graph_nhs_compar/index_9_1.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;2-create-a-graph&#34;&gt;2. Create a graph&lt;/h2&gt;
&lt;p&gt;We now know that the graph is bipartite, has multiple edges and has a directions, which means that we need to create MultiDiGraph.
We also have weight on edge, that is the number of prescriptions on flow and predicted items on flow for both of the used models.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;G1 = nx.from_pandas_edgelist(flow17, source=&#39;P_Code&#39;, target=&#39;D_Code_new&#39;, edge_attr=[&#39;N_Items&#39;], create_using=nx.MultiDiGraph())
print(&#39;Is the graph bipirtite?   &#39; + str(nx.is_bipartite(G1)))
print(&#39;Is the graph directed??   &#39; + str(nx.is_directed(G1)))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Is the graph bipirtite?   True
Is the graph directed??   True
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;G2 = nx.from_pandas_edgelist(flow17, source=&#39;P_Code&#39;, target=&#39;D_Code_new&#39;, edge_attr=[&#39;predict_items&#39;], create_using=nx.MultiDiGraph())
print(&#39;Is the graph bipirtite?   &#39; + str(nx.is_bipartite(G2)))
print(&#39;Is the graph directed??   &#39; + str(nx.is_directed(G2)))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Is the graph bipirtite?   True
Is the graph directed??   True
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;G3 = nx.from_pandas_edgelist(flow17, source=&#39;P_Code&#39;, target=&#39;D_Code_new&#39;, edge_attr=[&#39;predict_items_doubly&#39;], create_using=nx.MultiDiGraph())
print(&#39;Is the graph bipirtite?   &#39; + str(nx.is_bipartite(G3)))
print(&#39;Is the graph directed??   &#39; + str(nx.is_directed(G3)))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Is the graph bipirtite?   True
Is the graph directed??   True
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;3-look-at-the-properties-of-the-graph&#34;&gt;3. Look at the properties of the graph&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# Look at number on graph properties
nb_nodes = len(G1.nodes())
nb_arr = len(G1.edges())
print(&amp;quot;Number of nodes : &amp;quot; + str(nb_nodes))
print(&amp;quot;Number of edges : &amp;quot; + str(nb_arr))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Number of nodes : 485
Number of edges : 74668
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;degree_freq = np.array(nx.degree_histogram(G1)).astype(&#39;float&#39;)
plt.figure(figsize=(12, 8))
plt.stem(degree_freq)
plt.ylabel(&amp;quot;Frequence&amp;quot;)
plt.xlabel(&amp;quot;Degre&amp;quot;)
plt.show();
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;C:\Users\tk18583\AppData\Local\Continuum\anaconda3\envs\geonet\lib\site-packages\ipykernel_launcher.py:3: UserWarning:

In Matplotlib 3.3 individual lines on a stem plot will be added as a LineCollection instead of individual lines. This significantly improves the performance of a stem plot. To remove this warning and switch to the new behaviour, set the &amp;quot;use_line_collection&amp;quot; keyword argument to True.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./graph_nhs_compar/index_16_1.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;#degree_freq[degree_freq &amp;gt; 0]
x = degree_freq[degree_freq &amp;lt; 80]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;plt.figure(figsize=(12, 8))
plt.stem(x)
plt.ylabel(&amp;quot;Frequence&amp;quot;)
plt.xlabel(&amp;quot;Degre&amp;quot;)
plt.show();
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;C:\Users\tk18583\AppData\Local\Continuum\anaconda3\envs\geonet\lib\site-packages\ipykernel_launcher.py:2: UserWarning:

In Matplotlib 3.3 individual lines on a stem plot will be added as a LineCollection instead of individual lines. This significantly improves the performance of a stem plot. To remove this warning and switch to the new behaviour, set the &amp;quot;use_line_collection&amp;quot; keyword argument to True.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./graph_nhs_compar/index_18_1.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;
plt.figure(figsize=(14,14))
nx.draw_networkx(G1, with_labels=True, width=0.02)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;4-define-the-functions-to-calculate-centrality-and-snap-the-resluts-to-the-point-data&#34;&gt;4. Define the functions to calculate centrality and snap the resluts to the point data&lt;/h2&gt;
&lt;p&gt;The Bipirtate graph is very specific cas of graph and has its own dedicated centraliy functions that can be calculated with networkx package.&lt;/p&gt;
&lt;p&gt;This is the closeness and degree centrality, however none of those contain any weight, they are only based on the number of flows coming into the nodes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No centrality can be measured in weighted bipirtite graph&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Other measures we can look at are the Link Analysis measures. Those are Page Rank and HIIT (hubs and authorities). However, only Page Rank include a weight on edges,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Page Rank is the only measure we can apply on weighted directed bipartite graph!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def close_bi(graph, nodes, name, joinsA):
    # calculate the closenest
    close = bi.closeness_centrality(G = graph, nodes = nodes)
    # put it into a dataframe 
    x = pd.DataFrame.from_dict(close,  orient=&#39;index&#39;)
    x = x.reset_index()
    x = x.rename({0: name},axis = 1)

    df = joinsA.merge(x, left_on = &#39;P_Code&#39;, right_on = &#39;index&#39;, how = &#39;left&#39;)
    return df

def degree_bi(graph, nodes, name, joinsA, joinsB):
    # calculate the closenest
    close = bi.degree_centrality(G = graph, nodes = nodes)
    # put it into a dataframe 
    x = pd.DataFrame.from_dict(close,  orient=&#39;index&#39;)
    x = x.reset_index()
    x = x.rename({0: name},axis = 1)

    df = joinsA.merge(x, left_on = &#39;P_Code&#39;, right_on = &#39;index&#39;, how = &#39;left&#39;)
    df2 = joinsB.merge(x, left_on = &#39;D_Code_new&#39;, right_on = &#39;index&#39;, how = &#39;left&#39;)
    return df, df2

def pgr(graph, name, joinsA, joinsB, weight):
    # calculate the closenest
    close = nx.pagerank_scipy(G = graph, weight = weight, max_iter = 50)
    # put it into a dataframe 
    x = pd.DataFrame.from_dict(close,  orient=&#39;index&#39;)
    x = x.reset_index()
    x = x.rename({0: name},axis = 1)

    df = joinsA.merge(x, left_on = &#39;P_Code&#39;, right_on = &#39;index&#39;, how = &#39;left&#39;)
    df2 = joinsB.merge(x, left_on = &#39;D_Code_new&#39;, right_on = &#39;index&#39;, how = &#39;left&#39;)
    return df, df2

def pgr(graph, name, joinsA, joinsB, weight):
    # calculate the closenest
    close = nx.pagerank_scipy(G = graph, weight = weight, max_iter = 50)
    # put it into a dataframe 
    x = pd.DataFrame.from_dict(close,  orient=&#39;index&#39;)
    x = x.reset_index()
    x = x.rename({0: name},axis = 1)

    df = joinsA.merge(x, left_on = &#39;P_Code&#39;, right_on = &#39;index&#39;, how = &#39;left&#39;)
    df2 = joinsB.merge(x, left_on = &#39;D_Code_new&#39;, right_on = &#39;index&#39;, how = &#39;left&#39;)
    return df, df2

# eigenvector centrality does not work for multidigraphs
def eigen_ce(graph, name, joinsA, joinsB, weight):
    # calculate the closenest
    close = nx.eigenvector_centrality(G = graph, max_iter = 100, weight = weight)
    # put it into a dataframe 
    x = pd.DataFrame.from_dict(close,  orient=&#39;index&#39;)
    x = x.reset_index()
    x = x.rename({0: name},axis = 1)

    df = joinsA.merge(x, left_on = &#39;P_Code&#39;, right_on = &#39;index&#39;, how = &#39;left&#39;)
    df2 = joinsB.merge(x, left_on = &#39;D_Code_new&#39;, right_on = &#39;index&#39;, how = &#39;left&#39;)
    return df, df2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;5-apply-the-functions-for-bipartate-graph&#34;&gt;5. Apply the functions for Bipartate graph&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;GP2 = close_bi(graph = G1, nodes = list_flow_GP, name = &#39;close_G1&#39;, joinsA = GP)
GP2 = close_bi(graph = G2, nodes = list_flow_GP, name = &#39;close_G2&#39;, joinsA = GP2)
GP2 = close_bi(graph = G3, nodes = list_flow_GP, name = &#39;close_G3&#39;, joinsA = GP2)

GP2, PH2 = degree_bi(graph = G1, nodes = list_flow_GP, name = &#39;degree_G1&#39;, joinsA = GP2, joinsB = PH)
GP2, PH2 = degree_bi(graph = G2, nodes = list_flow_GP, name = &#39;degree_G2&#39;, joinsA = GP2, joinsB = PH2)
GP2, PH2 = degree_bi(graph = G3, nodes = list_flow_GP, name = &#39;degree_G3&#39;, joinsA = GP2, joinsB = PH2)

GP2, PH2 =  pgr(graph = G1, name = &#39;pgr_G1&#39;, weight = &#39;N_Items&#39;, joinsA = GP2, joinsB = PH2)
GP2, PH2 =  pgr(graph = G2, name = &#39;pgr_G2&#39;, weight = &#39;predict_items&#39;, joinsA = GP2, joinsB = PH2)
GP2, PH2 =  pgr(graph = G3, name = &#39;pgr_G3&#39;, weight = &#39;predict_items_doubly&#39;, joinsA = GP2, joinsB = PH2)

# eigenvector centrality does not work for multidigraphs
#GP2, PH2 =  eigen_ce(graph = G1, name = &#39;eig_G1&#39;, weight = &#39;N_Items&#39;, joinsA = GP2, joinsB = PH2)
#GP2, PH2 =  eigen_ce(graph = G2, name = &#39;eig_G2&#39;, weight = &#39;predict_items&#39;, joinsA = GP2, joinsB = PH2)
#GP2, PH2 =  eigen_ce(graph = G3, name = &#39;eig_G3&#39;, weight = &#39;predict_items_doubly&#39;, joinsA = GP2, joinsB = PH2)

GP2.drop([&#39;index_x&#39;,&#39;index_y&#39; ,&#39;index&#39;], axis=1, inplace=True)
PH2.drop([&#39;index_x&#39;,&#39;index_y&#39;], axis=1, inplace=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;PH2.info()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;class &#39;geopandas.geodataframe.GeoDataFrame&#39;&amp;gt;
Int64Index: 357 entries, 0 to 356
Data columns (total 25 columns):
 #   Column          Non-Null Count  Dtype   
---  ------          --------------  -----   
 0   D_Code          357 non-null    object  
 1   D_Name          357 non-null    object  
 2   D_full_address  357 non-null    object  
 3   D_Postcode2     357 non-null    object  
 4   D_Open_date     357 non-null    int64   
 5   D_Close_date    27 non-null     float64 
 6   D_Status_Code   357 non-null    object  
 7   D_Sub_type      357 non-null    object  
 8   D_Join          356 non-null    float64 
 9   D_Left          27 non-null     float64 
 10  D_X             357 non-null    float64 
 11  D_Y             357 non-null    float64 
 12  D_E             357 non-null    float64 
 13  D_N             357 non-null    float64 
 14  D_Setting       357 non-null    int64   
 15  D_Code_new      357 non-null    object  
 16  residents       357 non-null    float64 
 17  Density         357 non-null    float64 
 18  geometry        357 non-null    geometry
 19  degree_G1       357 non-null    float64 
 20  degree_G2       357 non-null    float64 
 21  degree_G3       357 non-null    float64 
 22  pgr_G1          357 non-null    float64 
 23  pgr_G2          357 non-null    float64 
 24  pgr_G3          357 non-null    float64 
dtypes: float64(15), geometry(1), int64(2), object(7)
memory usage: 72.5+ KB
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# look at the values
GP2.iloc[:,14:23].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;close_G1&lt;/th&gt;
      &lt;th&gt;close_G2&lt;/th&gt;
      &lt;th&gt;close_G3&lt;/th&gt;
      &lt;th&gt;degree_G1&lt;/th&gt;
      &lt;th&gt;degree_G2&lt;/th&gt;
      &lt;th&gt;degree_G3&lt;/th&gt;
      &lt;th&gt;pgr_G1&lt;/th&gt;
      &lt;th&gt;pgr_G2&lt;/th&gt;
      &lt;th&gt;pgr_G3&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;1.262397&lt;/td&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.456583&lt;/td&gt;
      &lt;td&gt;1.456583&lt;/td&gt;
      &lt;td&gt;1.456583&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;2.207283&lt;/td&gt;
      &lt;td&gt;2.207283&lt;/td&gt;
      &lt;td&gt;2.207283&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.834734&lt;/td&gt;
      &lt;td&gt;1.834734&lt;/td&gt;
      &lt;td&gt;1.834734&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.733894&lt;/td&gt;
      &lt;td&gt;1.733894&lt;/td&gt;
      &lt;td&gt;1.733894&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;1.262397&lt;/td&gt;
      &lt;td&gt;0.806723&lt;/td&gt;
      &lt;td&gt;0.806723&lt;/td&gt;
      &lt;td&gt;0.806723&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
      &lt;td&gt;0.001684&lt;/td&gt;
      &lt;td&gt;0.001684&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;PH2.iloc[:,19:25].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;degree_G1&lt;/th&gt;
      &lt;th&gt;degree_G2&lt;/th&gt;
      &lt;th&gt;degree_G3&lt;/th&gt;
      &lt;th&gt;pgr_G1&lt;/th&gt;
      &lt;th&gt;pgr_G2&lt;/th&gt;
      &lt;th&gt;pgr_G3&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;2.140625&lt;/td&gt;
      &lt;td&gt;2.140625&lt;/td&gt;
      &lt;td&gt;2.140625&lt;/td&gt;
      &lt;td&gt;0.002018&lt;/td&gt;
      &lt;td&gt;0.001812&lt;/td&gt;
      &lt;td&gt;0.001996&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;1.687500&lt;/td&gt;
      &lt;td&gt;1.687500&lt;/td&gt;
      &lt;td&gt;1.687500&lt;/td&gt;
      &lt;td&gt;0.002194&lt;/td&gt;
      &lt;td&gt;0.001957&lt;/td&gt;
      &lt;td&gt;0.002179&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;0.648438&lt;/td&gt;
      &lt;td&gt;0.648438&lt;/td&gt;
      &lt;td&gt;0.648438&lt;/td&gt;
      &lt;td&gt;0.001689&lt;/td&gt;
      &lt;td&gt;0.001774&lt;/td&gt;
      &lt;td&gt;0.001689&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;2.937500&lt;/td&gt;
      &lt;td&gt;2.937500&lt;/td&gt;
      &lt;td&gt;2.937500&lt;/td&gt;
      &lt;td&gt;0.002471&lt;/td&gt;
      &lt;td&gt;0.002330&lt;/td&gt;
      &lt;td&gt;0.002497&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;3.773438&lt;/td&gt;
      &lt;td&gt;3.773438&lt;/td&gt;
      &lt;td&gt;3.773438&lt;/td&gt;
      &lt;td&gt;0.001700&lt;/td&gt;
      &lt;td&gt;0.001867&lt;/td&gt;
      &lt;td&gt;0.001718&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 id=&#34;6-changing-graph-structure&#34;&gt;6. Changing graph structure&lt;/h2&gt;
&lt;p&gt;We can see that the degree centrality and closeness centraility is the same for all the graphs, because the wight is not included.&lt;/p&gt;
&lt;p&gt;The Pagerank for the GP&#39;s is not changing either as the flows are leaving them and page rank focuses on the incoming flows only.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Page Rank algorithm computes a ranking of the nodes in the graph G based on the structure of the incoming links, so we can only observe the importance of pharmacies.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;plt.figure(figsize=(30, 6))
# Degree Centrality
f, axarr = plt.subplots(1,3, num=1)

plt.sca(axarr[0])
PH2[&#39;pgr_G1&#39;].plot.hist(grid=True, bins=20, rwidth=0.9, color=&#39;#607c8e&#39;)
axarr[0].set_title(&#39;Page Rank Graph 1&#39;, size=16)

plt.sca(axarr[1])
PH2[&#39;pgr_G2&#39;].plot.hist(grid=True, bins=20, rwidth=0.9, color=&#39;#607c8e&#39;)
axarr[1].set_title(&#39;Page Rank Graph 2&#39;, size=16)

plt.sca(axarr[2])
PH2[&#39;pgr_G3&#39;].plot.hist(grid=True, bins=20, rwidth=0.9, color=&#39;#607c8e&#39;)
axarr[2].set_title(&#39;Page Rank Graph 3&#39;, size=16);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./graph_nhs_compar/index_28_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# which one is the most important? the one that sticks out
PH2[PH2[&#39;pgr_G1&#39;] &amp;gt; 0.0044]
&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;D_Code&lt;/th&gt;
      &lt;th&gt;D_Name&lt;/th&gt;
      &lt;th&gt;D_full_address&lt;/th&gt;
      &lt;th&gt;D_Postcode2&lt;/th&gt;
      &lt;th&gt;D_Open_date&lt;/th&gt;
      &lt;th&gt;D_Close_date&lt;/th&gt;
      &lt;th&gt;D_Status_Code&lt;/th&gt;
      &lt;th&gt;D_Sub_type&lt;/th&gt;
      &lt;th&gt;D_Join&lt;/th&gt;
      &lt;th&gt;D_Left&lt;/th&gt;
      &lt;th&gt;...&lt;/th&gt;
      &lt;th&gt;D_Code_new&lt;/th&gt;
      &lt;th&gt;residents&lt;/th&gt;
      &lt;th&gt;Density&lt;/th&gt;
      &lt;th&gt;geometry&lt;/th&gt;
      &lt;th&gt;degree_G1&lt;/th&gt;
      &lt;th&gt;degree_G2&lt;/th&gt;
      &lt;th&gt;degree_G3&lt;/th&gt;
      &lt;th&gt;pgr_G1&lt;/th&gt;
      &lt;th&gt;pgr_G2&lt;/th&gt;
      &lt;th&gt;pgr_G3&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;112&lt;/th&gt;
      &lt;td&gt;FLQ56&lt;/td&gt;
      &lt;td&gt;BOOTS UK LIMITED&lt;/td&gt;
      &lt;td&gt;BOOTS UK LIMITED,59 BROADMEAD,BRISTOL,nan,BS1 3ED&lt;/td&gt;
      &lt;td&gt;BS1 3ED&lt;/td&gt;
      &lt;td&gt;19480101&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;A&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;20040401.0&lt;/td&gt;
      &lt;td&gt;NaN&lt;/td&gt;
      &lt;td&gt;...&lt;/td&gt;
      &lt;td&gt;FLQ56&lt;/td&gt;
      &lt;td&gt;1021.5&lt;/td&gt;
      &lt;td&gt;202.028571&lt;/td&gt;
      &lt;td&gt;POINT (-2.58972 51.45774)&lt;/td&gt;
      &lt;td&gt;8.898438&lt;/td&gt;
      &lt;td&gt;8.898438&lt;/td&gt;
      &lt;td&gt;8.898438&lt;/td&gt;
      &lt;td&gt;0.004406&lt;/td&gt;
      &lt;td&gt;0.002691&lt;/td&gt;
      &lt;td&gt;0.003961&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;1 rows × 25 columns&lt;/p&gt;
&lt;/div&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;ax = plt.figure(figsize=(10, 10))
plt.axis([PH2[&#39;pgr_G1&#39;].min(), PH2[&#39;pgr_G1&#39;].max(), PH2[&#39;pgr_G1&#39;].min(), PH2[&#39;pgr_G1&#39;].max()])
# plots
plt.scatter(PH2[&#39;pgr_G1&#39;],PH2[&#39;pgr_G1&#39;])
plt.scatter(PH2[&#39;pgr_G1&#39;],PH2[&#39;pgr_G2&#39;])
plt.plot(PH2[&#39;pgr_G1&#39;].mean(),PH2[&#39;pgr_G1&#39;].mean(), c = &#39;r&#39;, marker = &#39;X&#39;, markersize = 30)
# regression plots
sns.regplot(PH2[&#39;pgr_G1&#39;],PH2[&#39;pgr_G1&#39;])
sns.regplot(PH2[&#39;pgr_G1&#39;],PH2[&#39;pgr_G2&#39;])

plt.legend([&#39;Mean&#39;,&#39;Page Rank from data&#39;, &#39;Page Rank from base gravity&#39;]);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./graph_nhs_compar/index_30_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;PH2[&#39;pgr_G1&#39;].mean()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;0.002197291434997281
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;ax = plt.figure(figsize=(10, 10))
plt.axis([PH2[&#39;pgr_G1&#39;].min(), PH2[&#39;pgr_G1&#39;].max(), PH2[&#39;pgr_G1&#39;].min(), PH2[&#39;pgr_G1&#39;].max()])
# plots
plt.scatter(PH2[&#39;pgr_G1&#39;],PH2[&#39;pgr_G1&#39;])
plt.scatter(PH2[&#39;pgr_G1&#39;],PH2[&#39;pgr_G3&#39;])
plt.plot(PH2[&#39;pgr_G1&#39;].mean(),PH2[&#39;pgr_G1&#39;].mean(), c = &#39;r&#39;, marker = &#39;X&#39;, markersize = 30)
# regression plots
sns.regplot(PH2[&#39;pgr_G1&#39;],PH2[&#39;pgr_G1&#39;])
sns.regplot(PH2[&#39;pgr_G1&#39;],PH2[&#39;pgr_G3&#39;])

plt.legend([&#39;Mean&#39;,&#39;Page Rank from data&#39;, &#39;Page Rank from doubly gravity model&#39; ]);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./graph_nhs_compar/index_32_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;
&lt;p&gt;What we can see on the figure above is how the node centrality in the graph conctructed from prediction relates to a node centrality in the graph constructed from raw data. In ideal situation, the line of best fit and the observations would follow the blue line, however, this is not the case. From the first figure, which looks at the importance of the nodes based on base gravity model (very simple one), you can observe that where the actual importannce of the node is high, the importance based on predicted flows drop down drastically, and where the actual importance of the node is low, the importance based on predicted flows is higher. You can also notice line of zeros trying to hide behind the y axes. So although the model predicts flow with certain accuracy, th quality of the flow is not the same as from the data. More specifically the model is not really able to see the hierarchical structure of the nodes and creates predictions that are close to mean for all nodes.&lt;/p&gt;
&lt;p&gt;Not suprisingly this is not the case for more complex gravity model such as doubly constrained gravity model. Here the hierarchy of the nodes is sustained by the prediction, however, that is expected as each node has it&#39;s own parameter in the model. Seems like a simple and smart solution, however, you need to remember the with doubly constrained model, where each node is considered as unique, predictions can be made only to existing nodes. So question like &amp;lsquo;What will happen with the flows if one of the GP&#39;s closes&amp;rsquo; are hardly answerable. It&#39;s like a cheeky shortcut; if I am not able to meodel complicated flow structure, let&#39;s model each node separately.&lt;/p&gt;
&lt;p&gt;Drop me a message on twitter if you have any comments :)&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Inter-National Migration; Part 2 - Calculating Distances</title>
      <link>https://lenkastatham.com/post/migration2.html</link>
      <pubDate>Thu, 09 Jan 2020 00:00:00 +0000</pubDate>
      <guid>https://lenkastatham.com/post/migration2.html</guid>
      <description>&lt;p&gt;&lt;strong&gt;This is a second part of Intra-National Migration data exploration. This post will also construt distance variables for the data and crack on with some Spatial Interction Modeling. This is a contribution to my PhD research that examines Origin-Destination and its spatial aspects.&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;
&lt;p&gt;UK intra-migration data are open source data that can be downloaded from &lt;a href=&#34;https://www.ons.gov.uk/peoplepopulationandcommunity/populationandmigration/migrationwithintheuk/datasets/internalmigrationbyoriginanddestinationlocalauthoritiessexandsingleyearofagedetailedestimatesdataset&#34;&gt;ONS&lt;/a&gt;. ONS uses the NHS Patient Register Data Service (PRDS) to find out the changes in patients adresses. Since most people change their address with their doctor soon after moving, these data are considered to provide a good proxy indicator of migration.&lt;/p&gt;
&lt;p&gt;The migration data is published every year and consist of Origin Local Authority ID, Destination Local Authority ID, sex, age and movement factor field. This movement factor is based on resscaled number of people on the flow.&lt;/p&gt;
&lt;p&gt;Please follow &lt;a href=&#34;https://www.ons.gov.uk/peoplepopulationandcommunity/populationandmigration/populationestimates/methodologies/methodologyguideformid2015ukpopulationestimatesenglandandwalesjune2016&#34;&gt;ONS&lt;/a&gt; to find more information about the methodology.&lt;/p&gt;
&lt;p&gt;Follow &lt;a href=&#34;https://lenkahas.com/post/migration.html&#34;&gt;Part 1&lt;/a&gt; to see some basic graphs of the data.&lt;/p&gt;
&lt;h1 id=&#34;recap&#34;&gt;Recap&lt;/h1&gt;
&lt;p&gt;I&#39;m just going to quickly recap what we know alrady about the data.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The origins and destinations are the same, UK Local Authorities.&lt;/li&gt;
&lt;li&gt;The flow volume is represented by movement factor wich is already scaled variable and is extremely skewed to the left.&lt;/li&gt;
&lt;li&gt;The year 2011 has very different nature from all the other years.&lt;/li&gt;
&lt;li&gt;Because we are not able to judstify the difference in nature of the 2011, we are going to work with the later years only.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;in-this-post&#34;&gt;In this post&amp;hellip;&lt;/h1&gt;
&lt;p&gt;I want to calculate distances between the Originn and Destination.&lt;/p&gt;
&lt;p&gt;Now, it might sound easy, but the theory behind this is more colmpicated than you would think.
Read through my &lt;a href=&#34;https://lenkahas.com/post/distance_decay.html&#34;&gt;other post&lt;/a&gt; that explains most of the theory behind this.&lt;/p&gt;
&lt;p&gt;I need to;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Calculate the Eclidian Distance between the Origins and Destinations&lt;/li&gt;
&lt;li&gt;Calculate the Great Circle Distance between the Origins and Destinations&lt;/li&gt;
&lt;li&gt;Calculate the Network Distance (walking path along the streets)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;1-calculate-the-euclidian-distance-between-the-origins-and-destinations&#34;&gt;1. Calculate the Euclidian Distance between the Origins and Destinations&lt;/h2&gt;
&lt;p&gt;To do that, we first need to load the libraries and the data we mungedin the post 1&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# we will first import all necessary libraries
import pandas as pd
import os
import numpy as np
import glob
from datetime import datetime
import geopandas as gpd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from shapely.geometry import Point, LineString
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# set the path
file = r&#39;./../data/migration/full_mig.csv&#39;
# read the file
df = pd.read_csv(file, index_col=None, low_memory=False)
# plot the top of the table
df.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;OutLA&lt;/th&gt;
      &lt;th&gt;InLA&lt;/th&gt;
      &lt;th&gt;Age&lt;/th&gt;
      &lt;th&gt;Sex&lt;/th&gt;
      &lt;th&gt;Moves&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;sex_bin&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;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;27&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.0002&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;36&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;0.0004&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;26&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.0004&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;25&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.0005&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;34&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;0.0014&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;1&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;# set the path
file = r&#39;./../data/migration/munged/LAcentrooids.shp&#39;
# read the file in
la = gpd.read_file(file)
# show the map
la.plot();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./migration2/index_6_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now I can create lat and long columns in the centrooid data which I&#39;ll merge to the flow data twice. First based on the Out-flow LA ID and second based on In-flow LA ID.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create new callumn x and y wit lat and long coordinates
la[&#39;x&#39;] = la.geometry.x
la[&#39;y&#39;] = la.geometry.y
la.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;objectid&lt;/th&gt;
      &lt;th&gt;lad17cd&lt;/th&gt;
      &lt;th&gt;lad17nm&lt;/th&gt;
      &lt;th&gt;geometry&lt;/th&gt;
      &lt;th&gt;x&lt;/th&gt;
      &lt;th&gt;y&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;1&lt;/td&gt;
      &lt;td&gt;E06000001&lt;/td&gt;
      &lt;td&gt;Hartlepool&lt;/td&gt;
      &lt;td&gt;POINT (-1.259479894328456 54.66947264221945)&lt;/td&gt;
      &lt;td&gt;-1.259480&lt;/td&gt;
      &lt;td&gt;54.669473&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;E06000002&lt;/td&gt;
      &lt;td&gt;Middlesbrough&lt;/td&gt;
      &lt;td&gt;POINT (-1.222265950359884 54.54199624640567)&lt;/td&gt;
      &lt;td&gt;-1.222266&lt;/td&gt;
      &lt;td&gt;54.541996&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;E06000003&lt;/td&gt;
      &lt;td&gt;Redcar and Cleveland&lt;/td&gt;
      &lt;td&gt;POINT (-1.020534867661266 54.55159000727666)&lt;/td&gt;
      &lt;td&gt;-1.020535&lt;/td&gt;
      &lt;td&gt;54.551590&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;E06000004&lt;/td&gt;
      &lt;td&gt;Stockton-on-Tees&lt;/td&gt;
      &lt;td&gt;POINT (-1.332307567288941 54.56157879321239)&lt;/td&gt;
      &lt;td&gt;-1.332308&lt;/td&gt;
      &lt;td&gt;54.561579&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;E06000005&lt;/td&gt;
      &lt;td&gt;Darlington&lt;/td&gt;
      &lt;td&gt;POINT (-1.552585765196361 54.54869927572448)&lt;/td&gt;
      &lt;td&gt;-1.552586&lt;/td&gt;
      &lt;td&gt;54.548699&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;# lets get rid of the rows we dont have spatial information for
# list of Unique ID&#39;s
d = list(la[&#39;lad17cd&#39;].unique())
# new dataset with just those that are in a list
df2 = df.loc[df[&#39;OutLA&#39;].isin(d)]
df3 = df2.loc[df2[&#39;InLA&#39;].isin(d)]
# see how many rows we lost
len(df) - len(df3)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;874611
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create two datasets from the la&#39;s base on Outflow and Inflow
# drop th geometries and create dataframes
outla = pd.DataFrame(la)
inla = pd.DataFrame(la)
# select just what we need
outla = outla.loc[:,[&#39;lad17cd&#39;,&#39;x&#39;,&#39;y&#39;]]
inla = inla.loc[:,[&#39;lad17cd&#39;,&#39;x&#39;,&#39;y&#39;]]
# rename the columns
outla.rename(columns = {&#39;x&#39;:&#39;out_x&#39;,&#39;y&#39;:&#39;out_y&#39;}, inplace = True)
inla.rename(columns = {&#39;x&#39;:&#39;in_x&#39;,&#39;y&#39;:&#39;in_y&#39;}, inplace = True)
# look at what we have
outla.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;lad17cd&lt;/th&gt;
      &lt;th&gt;out_x&lt;/th&gt;
      &lt;th&gt;out_y&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;E06000001&lt;/td&gt;
      &lt;td&gt;-1.259480&lt;/td&gt;
      &lt;td&gt;54.669473&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;E06000002&lt;/td&gt;
      &lt;td&gt;-1.222266&lt;/td&gt;
      &lt;td&gt;54.541996&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;E06000003&lt;/td&gt;
      &lt;td&gt;-1.020535&lt;/td&gt;
      &lt;td&gt;54.551590&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;E06000004&lt;/td&gt;
      &lt;td&gt;-1.332308&lt;/td&gt;
      &lt;td&gt;54.561579&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;E06000005&lt;/td&gt;
      &lt;td&gt;-1.552586&lt;/td&gt;
      &lt;td&gt;54.548699&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;# join the spatial points to the data, first for the OutLA and secondly for the InLA
df4 = df3.merge(outla, how = &#39;left&#39;, left_on = &#39;OutLA&#39;, right_on = &#39;lad17cd&#39;)
df4 = df4.merge(inla, how = &#39;left&#39;, left_on = &#39;InLA&#39;, right_on = &#39;lad17cd&#39;)
df4.tail()

&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;OutLA&lt;/th&gt;
      &lt;th&gt;InLA&lt;/th&gt;
      &lt;th&gt;Age&lt;/th&gt;
      &lt;th&gt;Sex&lt;/th&gt;
      &lt;th&gt;Moves&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;sex_bin&lt;/th&gt;
      &lt;th&gt;lad17cd_x&lt;/th&gt;
      &lt;th&gt;out_x&lt;/th&gt;
      &lt;th&gt;out_y&lt;/th&gt;
      &lt;th&gt;lad17cd_y&lt;/th&gt;
      &lt;th&gt;in_x&lt;/th&gt;
      &lt;th&gt;in_y&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;10701098&lt;/th&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;67&lt;/td&gt;
      &lt;td&gt;F&lt;/td&gt;
      &lt;td&gt;1.1416&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;-3.361057&lt;/td&gt;
      &lt;td&gt;51.740942&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;-3.409912&lt;/td&gt;
      &lt;td&gt;52.323737&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;10701099&lt;/th&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;73&lt;/td&gt;
      &lt;td&gt;F&lt;/td&gt;
      &lt;td&gt;0.9519&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;-3.361057&lt;/td&gt;
      &lt;td&gt;51.740942&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;-3.409912&lt;/td&gt;
      &lt;td&gt;52.323737&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;10701100&lt;/th&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;74&lt;/td&gt;
      &lt;td&gt;M&lt;/td&gt;
      &lt;td&gt;1.0020&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;-3.361057&lt;/td&gt;
      &lt;td&gt;51.740942&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;-3.409912&lt;/td&gt;
      &lt;td&gt;52.323737&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;10701101&lt;/th&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;74&lt;/td&gt;
      &lt;td&gt;F&lt;/td&gt;
      &lt;td&gt;0.9866&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;-3.361057&lt;/td&gt;
      &lt;td&gt;51.740942&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;-3.409912&lt;/td&gt;
      &lt;td&gt;52.323737&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;10701102&lt;/th&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;77&lt;/td&gt;
      &lt;td&gt;F&lt;/td&gt;
      &lt;td&gt;3.0000&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;-3.361057&lt;/td&gt;
      &lt;td&gt;51.740942&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;-3.409912&lt;/td&gt;
      &lt;td&gt;52.323737&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;# make sure there is no missing values
x = df4.isna()
x.loc[x[&#39;out_x&#39;] == True]
&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;OutLA&lt;/th&gt;
      &lt;th&gt;InLA&lt;/th&gt;
      &lt;th&gt;Age&lt;/th&gt;
      &lt;th&gt;Sex&lt;/th&gt;
      &lt;th&gt;Moves&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;sex_bin&lt;/th&gt;
      &lt;th&gt;lad17cd_x&lt;/th&gt;
      &lt;th&gt;out_x&lt;/th&gt;
      &lt;th&gt;out_y&lt;/th&gt;
      &lt;th&gt;lad17cd_y&lt;/th&gt;
      &lt;th&gt;in_x&lt;/th&gt;
      &lt;th&gt;in_y&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;That looks like exactly what we need!&lt;/p&gt;
&lt;p&gt;Next step will be calculating the Euclidian distance between the Drigin and the Destination.&lt;/p&gt;
&lt;p&gt;There are actually quite few way to do this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. You can just use pythagoras function to do that&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;test = df4
test[&#39;Euc_distance&#39;] = (((test.out_x - test.in_x) ** 2) + (test.out_y - test.in_y) ** 2) ** .5
test.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;OutLA&lt;/th&gt;
      &lt;th&gt;InLA&lt;/th&gt;
      &lt;th&gt;Age&lt;/th&gt;
      &lt;th&gt;Sex&lt;/th&gt;
      &lt;th&gt;Moves&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;sex_bin&lt;/th&gt;
      &lt;th&gt;lad17cd_x&lt;/th&gt;
      &lt;th&gt;out_x&lt;/th&gt;
      &lt;th&gt;out_y&lt;/th&gt;
      &lt;th&gt;lad17cd_y&lt;/th&gt;
      &lt;th&gt;in_x&lt;/th&gt;
      &lt;th&gt;in_y&lt;/th&gt;
      &lt;th&gt;Euc_distance&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;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;27&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.0002&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;36&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;0.0004&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;26&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.0004&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;25&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.0005&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;34&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;0.0014&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Very simple, we just need to pay attention to the units of the distance which is in degrees (coordinates of the points in degrees).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. You can use scipy.spatial.distance.cdist() function that will basically do the same job, however needs more data manipulation&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# import the library
# this could be done different ways too so feel free to do what suits you
import scipy.spatial.distance as ssd
# importing the library you use the the function as x=ssd.cdist()
from scipy.spatial import distance
# then use the function as x=distance.cdist()
import scipy
# then use the function as x=scipy.spatial.distance.cdist()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# Make selection
# I&#39;ll test this on selection of points as I did not find out why it does not like my vector of size 11575714...to big for this I guess
selection = test.iloc[0:5,:]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# to use this function, we neet to create numpy arrays from the coordinates, as the cdist() requires
coords_out = np.array(selection[[&#39;out_x&#39;,&#39;out_y&#39;]])
coords_in = np.array(selection[[&#39;in_x&#39;,&#39;in_y&#39;]])
# note that this function return a matrix
mat = distance.cdist(coords_out, coords_in, &#39;euclidean&#39;)
mat
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;array([[0.22773512, 0.22773512, 0.22773512, 0.22773512, 0.22773512],
       [0.22773512, 0.22773512, 0.22773512, 0.22773512, 0.22773512],
       [0.22773512, 0.22773512, 0.22773512, 0.22773512, 0.22773512],
       [0.22773512, 0.22773512, 0.22773512, 0.22773512, 0.22773512],
       [0.22773512, 0.22773512, 0.22773512, 0.22773512, 0.22773512]])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you need to convert the matrix into the collumn of the dataframe&amp;hellip;
I did not worked that one out, so let me know if you know how O:-)&lt;/p&gt;
&lt;p&gt;However, BIG ADVANTAGE of this function is that it has predefined 24 different calculation of distances. Check out the &lt;a href=&#34;https://docs.scipy.org/doc/scipy-0.17.0/reference/generated/scipy.spatial.distance.cdist.html&#34;&gt;documentation&lt;/a&gt;, and because it&#39;s based on C++ its quite fast.&lt;/p&gt;
&lt;h2 id=&#34;2-calculate-the-great-circle-distance-between-the-origins-and-destinations&#34;&gt;2. Calculate the Great Circle Distance between the Origins and Destinations&lt;/h2&gt;
&lt;p&gt;This could be also done several ways.&lt;/p&gt;
&lt;p&gt;The easiest way is using the GeoPy library, but if you find a faster way, let me know!&lt;/p&gt;
&lt;p&gt;To do this we need to get the coordinates in one column as it was in original point dataset just stripped out of the geometry column.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from geopy.distance import geodesic, great_circle
import itertools
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create stripped column of coordinates 
la[&#39;xy&#39;] = la.geometry.apply(lambda x: [x.y, x.x])

# turn it into dataframe
points = pd.DataFrame(la)
# select just what we need
points1 = points.loc[:,[&#39;lad17cd&#39;,&#39;xy&#39;]]
points2 = points.loc[:,[&#39;lad17cd&#39;,&#39;xy&#39;]]
# rename the columns
points1.rename(columns = {&#39;xy&#39;:&#39;xy_1&#39;}, inplace = True)
points2.rename(columns = {&#39;xy&#39;:&#39;xy_2&#39;}, inplace = True)
# join the spatial points to the data, first for the OutLA and secondly for the InLA
test2 = test.merge(points1, how = &#39;left&#39;, left_on = &#39;OutLA&#39;, right_on = &#39;lad17cd&#39;)
test2 = test2.merge(points2, how = &#39;left&#39;, left_on = &#39;InLA&#39;, right_on = &#39;lad17cd&#39;)
test2.tail()
&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;OutLA&lt;/th&gt;
      &lt;th&gt;InLA&lt;/th&gt;
      &lt;th&gt;Age&lt;/th&gt;
      &lt;th&gt;Sex&lt;/th&gt;
      &lt;th&gt;Moves&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;sex_bin&lt;/th&gt;
      &lt;th&gt;lad17cd_x&lt;/th&gt;
      &lt;th&gt;out_x&lt;/th&gt;
      &lt;th&gt;out_y&lt;/th&gt;
      &lt;th&gt;lad17cd_y&lt;/th&gt;
      &lt;th&gt;in_x&lt;/th&gt;
      &lt;th&gt;in_y&lt;/th&gt;
      &lt;th&gt;Euc_distance&lt;/th&gt;
      &lt;th&gt;lad17cd_x&lt;/th&gt;
      &lt;th&gt;xy_1&lt;/th&gt;
      &lt;th&gt;lad17cd_y&lt;/th&gt;
      &lt;th&gt;xy_2&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;10701098&lt;/th&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;67&lt;/td&gt;
      &lt;td&gt;F&lt;/td&gt;
      &lt;td&gt;1.1416&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;-3.361057&lt;/td&gt;
      &lt;td&gt;51.740942&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;-3.409912&lt;/td&gt;
      &lt;td&gt;52.323737&lt;/td&gt;
      &lt;td&gt;0.584839&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;[51.74094175880734, -3.361056696671829]&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;[52.323736681870706, -3.4099121042932894]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;10701099&lt;/th&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;73&lt;/td&gt;
      &lt;td&gt;F&lt;/td&gt;
      &lt;td&gt;0.9519&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;-3.361057&lt;/td&gt;
      &lt;td&gt;51.740942&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;-3.409912&lt;/td&gt;
      &lt;td&gt;52.323737&lt;/td&gt;
      &lt;td&gt;0.584839&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;[51.74094175880734, -3.361056696671829]&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;[52.323736681870706, -3.4099121042932894]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;10701100&lt;/th&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;74&lt;/td&gt;
      &lt;td&gt;M&lt;/td&gt;
      &lt;td&gt;1.0020&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;-3.361057&lt;/td&gt;
      &lt;td&gt;51.740942&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;-3.409912&lt;/td&gt;
      &lt;td&gt;52.323737&lt;/td&gt;
      &lt;td&gt;0.584839&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;[51.74094175880734, -3.361056696671829]&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;[52.323736681870706, -3.4099121042932894]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;10701101&lt;/th&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;74&lt;/td&gt;
      &lt;td&gt;F&lt;/td&gt;
      &lt;td&gt;0.9866&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;-3.361057&lt;/td&gt;
      &lt;td&gt;51.740942&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;-3.409912&lt;/td&gt;
      &lt;td&gt;52.323737&lt;/td&gt;
      &lt;td&gt;0.584839&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;[51.74094175880734, -3.361056696671829]&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;[52.323736681870706, -3.4099121042932894]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;10701102&lt;/th&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;77&lt;/td&gt;
      &lt;td&gt;F&lt;/td&gt;
      &lt;td&gt;3.0000&lt;/td&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;-3.361057&lt;/td&gt;
      &lt;td&gt;51.740942&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;-3.409912&lt;/td&gt;
      &lt;td&gt;52.323737&lt;/td&gt;
      &lt;td&gt;0.584839&lt;/td&gt;
      &lt;td&gt;W06000024&lt;/td&gt;
      &lt;td&gt;[51.74094175880734, -3.361056696671829]&lt;/td&gt;
      &lt;td&gt;W06000023&lt;/td&gt;
      &lt;td&gt;[52.323736681870706, -3.4099121042932894]&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;# now calculate the great circle distance
# this takes good 15 min
test2[&#39;great_circle_dist&#39;] = test2.apply(lambda x: great_circle(x.xy_1, x.xy_2).km, axis=1)
test2.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;OutLA&lt;/th&gt;
      &lt;th&gt;InLA&lt;/th&gt;
      &lt;th&gt;Age&lt;/th&gt;
      &lt;th&gt;Sex&lt;/th&gt;
      &lt;th&gt;Moves&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;sex_bin&lt;/th&gt;
      &lt;th&gt;lad17cd_x&lt;/th&gt;
      &lt;th&gt;out_x&lt;/th&gt;
      &lt;th&gt;out_y&lt;/th&gt;
      &lt;th&gt;lad17cd_y&lt;/th&gt;
      &lt;th&gt;in_x&lt;/th&gt;
      &lt;th&gt;in_y&lt;/th&gt;
      &lt;th&gt;Euc_distance&lt;/th&gt;
      &lt;th&gt;lad17cd_x&lt;/th&gt;
      &lt;th&gt;xy_1&lt;/th&gt;
      &lt;th&gt;lad17cd_y&lt;/th&gt;
      &lt;th&gt;xy_2&lt;/th&gt;
      &lt;th&gt;great_circle_dist&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;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;27&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.0002&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;[51.51484485100944, -0.09217115162643398]&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;[51.54527247765296, 0.13352209887032826]&lt;/td&gt;
      &lt;td&gt;15.97471&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;36&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;0.0004&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;[51.51484485100944, -0.09217115162643398]&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;[51.54527247765296, 0.13352209887032826]&lt;/td&gt;
      &lt;td&gt;15.97471&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;26&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.0004&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;[51.51484485100944, -0.09217115162643398]&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;[51.54527247765296, 0.13352209887032826]&lt;/td&gt;
      &lt;td&gt;15.97471&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;25&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.0005&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;[51.51484485100944, -0.09217115162643398]&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;[51.54527247765296, 0.13352209887032826]&lt;/td&gt;
      &lt;td&gt;15.97471&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;34&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;0.0014&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;[51.51484485100944, -0.09217115162643398]&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;[51.54527247765296, 0.13352209887032826]&lt;/td&gt;
      &lt;td&gt;15.97471&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;YAAAAAAAAY&lt;/p&gt;
&lt;p&gt;We have got a distance in kilometres!&lt;/p&gt;
&lt;p&gt;Ok it&#39;s a mess, let&#39;s clean the dataset a little bit as there is several extra columns.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create back up first
test3 = test2
test3 = test3[[&#39;OutLA&#39;,&#39;InLA&#39;,&#39;Age&#39;,&#39;Moves&#39;,&#39;Date&#39;,&#39;sex_bin&#39;, &#39;Euc_distance&#39;,&#39;great_circle_dist&#39;,&#39;out_x&#39;, &#39;out_y&#39;,&#39;in_x&#39;,&#39;in_y&#39;, &#39;xy_1&#39;,&#39;xy_2&#39;]]
test3.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;OutLA&lt;/th&gt;
      &lt;th&gt;InLA&lt;/th&gt;
      &lt;th&gt;Age&lt;/th&gt;
      &lt;th&gt;Moves&lt;/th&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;sex_bin&lt;/th&gt;
      &lt;th&gt;Euc_distance&lt;/th&gt;
      &lt;th&gt;great_circle_dist&lt;/th&gt;
      &lt;th&gt;out_x&lt;/th&gt;
      &lt;th&gt;out_y&lt;/th&gt;
      &lt;th&gt;in_x&lt;/th&gt;
      &lt;th&gt;in_y&lt;/th&gt;
      &lt;th&gt;xy_1&lt;/th&gt;
      &lt;th&gt;xy_2&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;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;27&lt;/td&gt;
      &lt;td&gt;0.0002&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
      &lt;td&gt;15.97471&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;[51.51484485100944, -0.09217115162643398]&lt;/td&gt;
      &lt;td&gt;[51.54527247765296, 0.13352209887032826]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;36&lt;/td&gt;
      &lt;td&gt;0.0004&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
      &lt;td&gt;15.97471&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;[51.51484485100944, -0.09217115162643398]&lt;/td&gt;
      &lt;td&gt;[51.54527247765296, 0.13352209887032826]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;26&lt;/td&gt;
      &lt;td&gt;0.0004&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
      &lt;td&gt;15.97471&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;[51.51484485100944, -0.09217115162643398]&lt;/td&gt;
      &lt;td&gt;[51.54527247765296, 0.13352209887032826]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;25&lt;/td&gt;
      &lt;td&gt;0.0005&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
      &lt;td&gt;15.97471&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;[51.51484485100944, -0.09217115162643398]&lt;/td&gt;
      &lt;td&gt;[51.54527247765296, 0.13352209887032826]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;E09000001&lt;/td&gt;
      &lt;td&gt;E09000002&lt;/td&gt;
      &lt;td&gt;34&lt;/td&gt;
      &lt;td&gt;0.0014&lt;/td&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;0.227735&lt;/td&gt;
      &lt;td&gt;15.97471&lt;/td&gt;
      &lt;td&gt;-0.092171&lt;/td&gt;
      &lt;td&gt;51.514845&lt;/td&gt;
      &lt;td&gt;0.133522&lt;/td&gt;
      &lt;td&gt;51.545272&lt;/td&gt;
      &lt;td&gt;[51.51484485100944, -0.09217115162643398]&lt;/td&gt;
      &lt;td&gt;[51.54527247765296, 0.13352209887032826]&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 id=&#34;3-calculating-the-network-distances&#34;&gt;3. Calculating the network distances&lt;/h2&gt;
&lt;p&gt;I will demonstrate this on random points on Bristol as our migration data are not that relevant to a network distance.
We just need to upload two libraries osmnx and networkx&lt;/p&gt;
&lt;p&gt;There is nothing easier than get your network from osmnx, no need for looking for data and extra faf&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import osmnx as ox
import networkx as nx
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define the place
place_name = &amp;quot;Bristol, United Kingdom&amp;quot;
# define the graph
graph = ox.graph_from_place(place_name, network_type=&#39;drive&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Now get some random points from Bristol&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# 51.453700, -2.595778 # Dominos pizza in Bristol city centre
# 51.456294, -2.605236 # Bristol Gallery and Museum
# 51.468233, -2.588812 # Montpelier rail station
# 51.456569, -2.626453 # Clifton observatory

# create the list of coordinates
lat_list = [51.453700, 51.456294, 51.468233, 51.456569]
lon_list = [-2.595778, -2.605236, -2.588812, -2.626453]

#put it together
random_points = zip(lon_list, lat_list)

# create dataframe
random_points = pd.DataFrame(random_points)
# change the names of columns
random_points.rename(columns = {0:&#39;x&#39;,1:&#39;y&#39;}, inplace = True)

# create spatial dataframe from the dataframe
random_points_gpd = gpd.GeoDataFrame(
    random_points, geometry=gpd.points_from_xy(random_points.x, random_points.y))
# assign coordinates
random_points_gpd.crs = la.crs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Now we need to convert everything to UTM as we want to get the lenght in metres not degrees&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# define the crs
utm = &amp;quot;+proj=utm +zone=33 +ellps=WGS84 +datum=WGS84 +units=m +no_defs&amp;quot;
# points to utm
random_points_gpd = random_points_gpd.to_crs(utm)
# convert the graph into utm...projct_graph function does it all
graph = ox.project_graph(graph, to_crs = utm)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# lets just see how it looks like
fig, ax = ox.plot_graph(graph_proj)
plt.tight_layout()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;./migration2/index_34_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Figure size 432x288 with 0 Axes&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# separate the nodes and edges from the graph
nodes_proj, edges_proj = ox.graph_to_gdfs(graph_proj, nodes=True, edges=True)
# look at the crs just to check
print(&amp;quot;Coordinate system:&amp;quot;, edges_proj.crs)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Coordinate system: +proj=utm +zone=33 +ellps=WGS84 +datum=WGS84 +units=m +no_defs
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create nearest nodes for each point

#get the coordinates only into dataframe
random_points_gpd[&#39;a&#39;] = random_points_gpd.geometry.y
random_points_gpd[&#39;b&#39;] = random_points_gpd.geometry.x
target_xy = pd.DataFrame(random_points_gpd[[&#39;a&#39;,&#39;b&#39;]])

# get the nearest node id for each row
def nearest_node(Lat,Lon):
    nearest_node,dist=ox.get_nearest_node(graph, (Lat,Lon), return_dist=True)  
    return nearest_node 

# apply the function
target_xy[&#39;node&#39;] = np.vectorize(nearest_node)(target_xy[&#39;a&#39;],target_xy[&#39;b&#39;])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Now we create the Origin-Destination data&lt;/strong&gt; this is going to be little fidly, indeed, but at least we have just a sample dataset&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# separate just the node id
target2 = target_xy.iloc[:,[2]]

# double this for destinations
target3 = target_xy.iloc[:,[2]]
# reset the index and reverse the order
target3 = target3.sort_index(ascending=False).reset_index().reset_index()
target2 = target2.reset_index()

# merge those two node datasets into OD data
OD = target2.merge(target3, left_on=&#39;index&#39;, right_on= &#39;level_0&#39;)

# get rid of unnecessary columns
OD = OD.loc[:,[&#39;node_x&#39;,&#39;node_y&#39;]]
OD.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;node_x&lt;/th&gt;
      &lt;th&gt;node_y&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;6502760749&lt;/td&gt;
      &lt;td&gt;26127008&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;5432376184&lt;/td&gt;
      &lt;td&gt;358536159&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;358536159&lt;/td&gt;
      &lt;td&gt;5432376184&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;26127008&lt;/td&gt;
      &lt;td&gt;6502760749&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;# define a function that calculates shortest path between the nodes on graph
def short_path_length(row):
    return nx.shortest_path_length(graph, row[&#39;node_x&#39;], row[&#39;node_y&#39;], weight=&#39;length&#39;)

# apply the function to our OD data.....Note: as we are working with UTM projection, the units should be metres
OD[&#39;short_path_length&#39;] = OD.apply(short_path_length, axis=1)
OD.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;node_x&lt;/th&gt;
      &lt;th&gt;node_y&lt;/th&gt;
      &lt;th&gt;short_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;6502760749&lt;/td&gt;
      &lt;td&gt;26127008&lt;/td&gt;
      &lt;td&gt;4537.728&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;5432376184&lt;/td&gt;
      &lt;td&gt;358536159&lt;/td&gt;
      &lt;td&gt;4031.132&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;358536159&lt;/td&gt;
      &lt;td&gt;5432376184&lt;/td&gt;
      &lt;td&gt;4039.488&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;26127008&lt;/td&gt;
      &lt;td&gt;6502760749&lt;/td&gt;
      &lt;td&gt;4611.492&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Bare in mind&lt;/strong&gt; I calculated the network distances on sample points in small area. That is because graph of whole UK is just huuuge and the computation would be time-expensive. In my opinion, we do not need the network distance for the migration data, however it could be usefull to know how to do that, so let me know if you found any quick and efficient way to do that.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
