<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>EDA | Lenka&#39;s blog</title>
    <link>https://lenkastatham.com/tags/eda.html</link>
      <atom:link href="https://lenkastatham.com/tags/eda/index.xml" rel="self" type="application/rss+xml" />
    <description>EDA</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-gb</language><lastBuildDate>Mon, 30 Dec 2019 00:00:00 +0000</lastBuildDate>
    <image>
      <url>https://lenkastatham.com/img/icon-192.png</url>
      <title>EDA</title>
      <link>https://lenkastatham.com/tags/eda.html</link>
    </image>
    
    <item>
      <title>Intra-National Migration, Part 1 - EDA</title>
      <link>https://lenkastatham.com/post/migration.html</link>
      <pubDate>Mon, 30 Dec 2019 00:00:00 +0000</pubDate>
      <guid>https://lenkastatham.com/post/migration.html</guid>
      <description>&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;
&lt;h2 id=&#34;what-do-i-do-here&#34;&gt;What do I do here?&lt;/h2&gt;
&lt;p&gt;This post is an exploration of the UK intra-migration data. Those 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;h2 id=&#34;why-do-i-do-this&#34;&gt;Why do I do this?&lt;/h2&gt;
&lt;p&gt;My PhD research is looking at origin-destination flow data and explores Machine Learning modelling possibilities, considering the spatial structure of the flows, including the scale. The ONS migration data is one of the datasets I&#39;m going to use for modelling. Thus, Exploration Data Analysis is a vital process of the reserach.&lt;/p&gt;
&lt;h2 id=&#34;how&#34;&gt;How?&lt;/h2&gt;
&lt;p&gt;I&#39;ll be looking at&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The nature of the variables&lt;/li&gt;
&lt;li&gt;The structure of the migration in time&lt;/li&gt;
&lt;li&gt;The structure of the migration in space&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To do that I need to&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Load the data in and join them all into one dataset&lt;/li&gt;
&lt;li&gt;Make sure we retain the date to look at the timestamp&lt;/li&gt;
&lt;li&gt;Join a spatial information to ensure the validity of all origins and destinations&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;1-load-the-data-in-and-join-them-all-into-one-dataset--2-make-sure-we-retain-the-date-to-look-at-the-timestamp&#34;&gt;1. Load the data in and join them all into one dataset + 2. Make sure we retain the date to look at the timestamp&lt;/h2&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
import datetime 
import datetime as dt
from datetime import datetime
import geopandas as gpd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# set the path
path = r&#39;./../../data/migration&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;os.chdir(path) # navigate to path

all_files = glob.glob(path + &amp;quot;/*.csv&amp;quot;) # access all the csv files in the folder and create list

li = [] # create empty list

# function which reads each csv, set up same header for each, create a new column with name of each file and store them in a list
for filename in all_files:
    df = pd.read_csv(filename, index_col=None, header=0, names =[&#39;OutLA&#39;, &#39;InLA&#39;, &#39;Age&#39;, &#39;Sex&#39;, &#39;Moves&#39;], low_memory=False)
    df[&#39;filename&#39;] = filename
    li.append(df)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# rowbind (concatonate) all the csv&#39;s in a list together
frame = pd.concat(li, axis=0, ignore_index=True, sort=False)

# seperate just the year from the filename variable
frame[&#39;filename&#39;] = frame[&#39;filename&#39;].str[23:] # this number depends on how long is your path, this row deletes 23 characters from left
frame[&#39;filename&#39;] = frame[&#39;filename&#39;].str[:-9] # and this row deletes 9 characters from right

# convert the year to date
frame[&#39;filename&#39;] = frame[&#39;filename&#39;].apply(lambda x: 
                                    dt.datetime.strptime(x,&#39;%Y&#39;))
# rename the date collumn
frame = frame.rename({&#39;filename&#39;:&#39;Date&#39;},axis = 1)

# fix the sex column - convert all to binary
frame.loc[frame[&#39;Sex&#39;]==1,&#39;sex_bin&#39;] = 1
frame.loc[frame[&#39;Sex&#39;]==2,&#39;sex_bin&#39;] = 2
frame.loc[frame[&#39;Sex&#39;]==&#39;M&#39;,&#39;sex_bin&#39;] = 1
frame.loc[frame[&#39;Sex&#39;]==&#39;F&#39;,&#39;sex_bin&#39;] = 2
frame[&#39;sex_bin&#39;].fillna(&#39;Other&#39;, inplace=True)
frame[&#39;sex_bin&#39;] = frame[&#39;sex_bin&#39;].astype(int)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# set the path
path = r&#39;./../../../post/migration&#39;
# fix the path
os.chdir(path) # navigate to path
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# Write the cleaned dataset to back it up if we need
# frame.to_csv(index = False , path_or_buf = &#39;./../../data/migration/full_migration.csv&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# now I just create a backup in the environment so I don&#39;t need to go through all the steps above in case I do something wrong
df = frame
df = df.set_index(&#39;Date&#39;) # set the date as an index
df.head(3) # look at the first 3 rows
&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;sex_bin&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Date&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;2011-01-01&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;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2011-01-01&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;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2011-01-01&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;2&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;You can see how the data looks like here. There is 7 collumn, from which 1st is the date, 2nd and 3rd retain the ID of the Origin and Destination Local Authority. There are two demographic variables, age and sex. The sex_bin is the sex variable in binary format, as the original variable was combination of Binary and characters. Most important variables is the &amp;lsquo;Moves&amp;rsquo; that represents the amount of moves in one flux. By flux, I mean unique flow defined by the combination of Origin, Destination, Date, Age and Sex.&lt;/p&gt;
&lt;p&gt;From the cell below you can see that the dataset has 11 milion rows combining the migration data from 2011 till 2018.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;df.info()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;class &#39;pandas.core.frame.DataFrame&#39;&amp;gt;
DatetimeIndex: 11575714 entries, 2011-01-01 to 2018-01-01
Data columns (total 6 columns):
OutLA      object
InLA       object
Age        int64
Sex        object
Moves      float64
sex_bin    int32
dtypes: float64(1), int32(1), int64(1), object(3)
memory usage: 574.1+ MB
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# Use seaborn style defaults and set the default figure size
sns.set(rc={&#39;figure.figsize&#39;:(10, 4)})
# Plot the moves as a time series
df[&#39;Moves&#39;].plot();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_12_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Well that is not very pretty. As the date of the flow is always the 1st day of the 1st month, the time series expect that the rest of th month is zero and show that on the graph. To avoid that, let&#39;s try group the values by year.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create new variable with grouped values
yearly = df.groupby(df.index).mean()
# use the same plot as before
sns.set(rc={&#39;figure.figsize&#39;:(10, 4)})
yearly[&#39;Moves&#39;].plot();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_14_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;This looks like the first recorded year 2011 had less then half of the migration flows as the years after. This could be very much true, but lets have a look at the plot witout the 2011.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create new variable with grouped values
yearly = df.groupby(df.index).mean()
yearly = yearly[1:8] # remember the first row has index 0
# use the same plot as before
sns.set(rc={&#39;figure.figsize&#39;:(8, 4)})
yearly[&#39;Moves&#39;].plot();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_16_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Other than very low migration rate in 2011, there is a certainly an overall increase of the migration between 2016 and 2018.&lt;/p&gt;
&lt;p&gt;We can have a look at the distribution of genders in the migration flows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# get percentages of each gender throughout the dataset
sb = df[&#39;sex_bin&#39;].value_counts(normalize = True)
# create dataframe
sb = pd.DataFrame(sb)
# rename the value collumn
sb = sb.rename({&#39;sex_bin&#39;:&#39;percentage&#39;},axis = 1)
# create new value for sex column
sex = [&#39;Female&#39;, &#39;Male&#39;]
# add the new column to dataframe
sb[&#39;sex&#39;] = sex
# change index for the new column
sb = sb.set_index(&#39;sex&#39;)
# see how the table looks like
sb
&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;percentage&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;sex&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;Female&lt;/th&gt;
      &lt;td&gt;0.506348&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Male&lt;/th&gt;
      &lt;td&gt;0.493652&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;ax = sb.plot(kind=&#39;bar&#39;, title =&amp;quot;Gender structure&amp;quot;, figsize=(5, 3), legend=False, fontsize=12)
ax.set_xlabel(&amp;quot;Sex&amp;quot;, fontsize=12)
ax.set_ylabel(&amp;quot;Percentage from all flows&amp;quot;, fontsize=12)
plt.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_19_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Looks like there is slightly more women than men moving within the UK. Is it true for all of the years?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# group by the date and sex
sb_year = frame.groupby([&#39;Date&#39;, &#39;sex_bin&#39;])
# create variable from the grouping
sb_year = sb_year.size()
# create dataframe
sb_year = pd.DataFrame(sb_year)
# rename the value collumn
sb_year = sb_year.rename({0:&#39;frequency&#39;},axis = 1)
# create new value for sex column
sex = [&#39;Male&#39;, &#39;Female&#39;,&#39;Male&#39;, &#39;Female&#39;,&#39;Male&#39;, &#39;Female&#39;,&#39;Male&#39;, &#39;Female&#39;,&#39;Male&#39;, &#39;Female&#39;,&#39;Male&#39;, &#39;Female&#39;,&#39;Male&#39;, &#39;Female&#39;,&#39;Male&#39;, &#39;Female&#39;]
# add the new column to dataframe
sb_year[&#39;sex&#39;] = sex
# reset index
sb_year = sb_year.reset_index()
sb_year
&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;Date&lt;/th&gt;
      &lt;th&gt;sex_bin&lt;/th&gt;
      &lt;th&gt;frequency&lt;/th&gt;
      &lt;th&gt;sex&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;2011-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;1233198&lt;/td&gt;
      &lt;td&gt;Male&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;/th&gt;
      &lt;td&gt;2011-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;1241611&lt;/td&gt;
      &lt;td&gt;Female&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;/th&gt;
      &lt;td&gt;2012-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;611320&lt;/td&gt;
      &lt;td&gt;Male&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;/th&gt;
      &lt;td&gt;2012-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;625818&lt;/td&gt;
      &lt;td&gt;Female&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;/th&gt;
      &lt;td&gt;2013-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;616615&lt;/td&gt;
      &lt;td&gt;Male&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;5&lt;/th&gt;
      &lt;td&gt;2013-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;634451&lt;/td&gt;
      &lt;td&gt;Female&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;6&lt;/th&gt;
      &lt;td&gt;2014-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;635949&lt;/td&gt;
      &lt;td&gt;Male&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;7&lt;/th&gt;
      &lt;td&gt;2014-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;656613&lt;/td&gt;
      &lt;td&gt;Female&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;8&lt;/th&gt;
      &lt;td&gt;2015-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;632116&lt;/td&gt;
      &lt;td&gt;Male&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;9&lt;/th&gt;
      &lt;td&gt;2015-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;651020&lt;/td&gt;
      &lt;td&gt;Female&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;10&lt;/th&gt;
      &lt;td&gt;2016-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;632000&lt;/td&gt;
      &lt;td&gt;Male&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;11&lt;/th&gt;
      &lt;td&gt;2016-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;650336&lt;/td&gt;
      &lt;td&gt;Female&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;12&lt;/th&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;673271&lt;/td&gt;
      &lt;td&gt;Male&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;13&lt;/th&gt;
      &lt;td&gt;2017-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;698670&lt;/td&gt;
      &lt;td&gt;Female&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;14&lt;/th&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;679906&lt;/td&gt;
      &lt;td&gt;Male&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;15&lt;/th&gt;
      &lt;td&gt;2018-01-01&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;702820&lt;/td&gt;
      &lt;td&gt;Female&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;fig, ax = plt.subplots(figsize=(12, 8))
x = np.arange(len(sb_year.Date.unique()))

# Define bar width. We&#39;ll use this to offset the second bar.
bar_width = 0.4

# Note we add the `width` parameter now which sets the width of each bar.
b1 = ax.bar(x, sb_year.loc[sb_year[&#39;sex&#39;] == &#39;Male&#39;, &#39;frequency&#39;],
            width=bar_width, label = &#39;Man&#39;)
# Same thing, but offset the x by the width of the bar.
b2 = ax.bar(x + bar_width, sb_year.loc[sb_year[&#39;sex&#39;] == &#39;Female&#39;, &#39;frequency&#39;],
            width=bar_width, label = &#39;Woman&#39;)

# Fix the x-axes.
ax.set_xticks(x + bar_width / 2)
ax.set_xticklabels(sb_year.Date.unique(), rotation=45)

# Add legend.
ax.legend()

# Axis styling.
ax.spines[&#39;top&#39;].set_visible(False)
ax.spines[&#39;right&#39;].set_visible(False)
ax.spines[&#39;left&#39;].set_visible(False)
ax.spines[&#39;bottom&#39;].set_color(&#39;#DDDDDD&#39;)
ax.tick_params(bottom=False, left=False)
ax.set_axisbelow(True)
ax.yaxis.grid(True, color=&#39;#EEEEEE&#39;)
ax.xaxis.grid(False)

# Add axis and chart labels.
ax.set_xlabel(&#39;Year&#39;, labelpad=15)
ax.set_ylabel(&#39;Count&#39;, labelpad=15)
ax.set_title(&#39;UK Intra-Migrants by the gender&#39;, pad=15)

fig.tight_layout()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_22_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;It is definitely true that more woman move within UK than man, even for each year.&lt;/p&gt;
&lt;p&gt;Bellow you can see how the difference between the genders differ in each year.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# separate the Males and Females into seperate datasets
M = sb_year.loc[sb_year[&#39;sex&#39;] == &#39;Male&#39;]
F = sb_year.loc[sb_year[&#39;sex&#39;] == &#39;Female&#39;]
# merge those two datasets
X = M.merge(F, left_on = &#39;Date&#39;, right_on = &#39;Date&#39;)
# calculate difference between males and females
X[&#39;Diff&#39;] = X[&#39;frequency_y&#39;] - X[&#39;frequency_x&#39;]
# choose just desired columns
XX = X.loc[:,[&#39;Date&#39;,&#39;Diff&#39;]]
# plot the time-series of the gender difference
sns.set(rc={&#39;figure.figsize&#39;:(8, 4)})
XX[&#39;Diff&#39;].plot();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_24_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;wait-a-second-&#34;&gt;Wait a second &amp;hellip;&lt;/h3&gt;
&lt;p&gt;It looks like in 2011, the frequency of the flows is much higher than all the other years, however, from the graphs above showing the &amp;lsquo;Moves&amp;rsquo; factor, we know that the 2011 actually has visibly lower movement factor. In other words, although the 2011 has the most amount of unique fluxes, the amount of people moving is much lower than in recent years.&lt;/p&gt;
&lt;p&gt;This is an interesting discovery, but what could be a cause?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Any change in methodology between 2011 and 2012.&lt;/li&gt;
&lt;li&gt;Differences in the structure of the data before and after 2011&lt;/li&gt;
&lt;li&gt;Change in the nature of the data. I could be that people changed their behaviour pattern.&lt;/li&gt;
&lt;li&gt;Could it be any mistake from my side?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&#39;s investigate all options.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Changes in the methodology&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The only change in methodology found in the &lt;a href=&#34;https://www.ons.gov.uk/peoplepopulationandcommunity/populationandmigration/populationestimates/methodologies/methodologyguideformid2015ukpopulationestimatesenglandandwalesjune2016#toc&#34;&gt;methods guide&lt;/a&gt; is this;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;ldquo;For the mid-2012 to mid-2016 internal migration estimates we improved our methods by linking the health registration data with data from the Higher Education Statistics Agency (HESA). The HESA data showed where students were registered by their university as living, and this allowed us to make more accurate estimates of people moving to study in each area.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Difference in the data structure&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From the &lt;a href=&#34;https://www.ons.gov.uk/peoplepopulationandcommunity/populationandmigration/migrationwithintheuk/datasets/internalmigrationbyoriginanddestinationlocalauthoritiessexandsingleyearofagedetailedestimatesdataset&#34;&gt;download site&lt;/a&gt; we can see that the 2011 dataset is the only one that has 3 parts, it has the highest number of rows (unique fluxes). It contains 2.47 milion records, which is almost half as much as 2018 dataset with its 1.38 milion records.&lt;/p&gt;
&lt;p&gt;Is it that the 2011 dataset contains more areas than the later one?&lt;/p&gt;
&lt;h2 id=&#34;3-join-a-spatial-information-to-the-data&#34;&gt;3. Join a spatial information to the data&lt;/h2&gt;
&lt;p&gt;Let&#39;s join in a spatial data to see it on a map. There are several datasets that could be used for this, for example &lt;a href=&#34;https://geoportal.statistics.gov.uk/datasets/ae90afc385c04d869bc8cf8890bd1bcd_1&#34;&gt;ONS LA lookup&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# set the path
file = r&#39;./../data/Local_Authority_Districts_December_2017_Full_Clipped_Boundaries_in_Great_Britain/Local_Authority_Districts_December_2017_Full_Clipped_Boundaries_in_Great_Britain.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;migration/index_28_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# look at the data
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;lad17nmw&lt;/th&gt;
      &lt;th&gt;bng_e&lt;/th&gt;
      &lt;th&gt;bng_n&lt;/th&gt;
      &lt;th&gt;long&lt;/th&gt;
      &lt;th&gt;lat&lt;/th&gt;
      &lt;th&gt;st_areasha&lt;/th&gt;
      &lt;th&gt;st_lengths&lt;/th&gt;
      &lt;th&gt;geometry&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;0&lt;/th&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;E06000001&lt;/td&gt;
      &lt;td&gt;Hartlepool&lt;/td&gt;
      &lt;td&gt;None&lt;/td&gt;
      &lt;td&gt;447157&lt;/td&gt;
      &lt;td&gt;531476&lt;/td&gt;
      &lt;td&gt;-1.27023&lt;/td&gt;
      &lt;td&gt;54.676159&lt;/td&gt;
      &lt;td&gt;9.355951e+07&lt;/td&gt;
      &lt;td&gt;71707.407523&lt;/td&gt;
      &lt;td&gt;(POLYGON ((447213.8995000003 537036.1042999998...&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;None&lt;/td&gt;
      &lt;td&gt;451141&lt;/td&gt;
      &lt;td&gt;516887&lt;/td&gt;
      &lt;td&gt;-1.21099&lt;/td&gt;
      &lt;td&gt;54.544670&lt;/td&gt;
      &lt;td&gt;5.388858e+07&lt;/td&gt;
      &lt;td&gt;43840.866561&lt;/td&gt;
      &lt;td&gt;(POLYGON ((448958.9007000001 521835.6952999998...&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;None&lt;/td&gt;
      &lt;td&gt;464359&lt;/td&gt;
      &lt;td&gt;519597&lt;/td&gt;
      &lt;td&gt;-1.00611&lt;/td&gt;
      &lt;td&gt;54.567520&lt;/td&gt;
      &lt;td&gt;2.448203e+08&lt;/td&gt;
      &lt;td&gt;97993.391012&lt;/td&gt;
      &lt;td&gt;(POLYGON ((455752.6002000002 528195.7048000004...&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;None&lt;/td&gt;
      &lt;td&gt;444937&lt;/td&gt;
      &lt;td&gt;518183&lt;/td&gt;
      &lt;td&gt;-1.30669&lt;/td&gt;
      &lt;td&gt;54.556911&lt;/td&gt;
      &lt;td&gt;2.049622e+08&lt;/td&gt;
      &lt;td&gt;119581.595543&lt;/td&gt;
      &lt;td&gt;(POLYGON ((444157.0018999996 527956.3033000007...&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;None&lt;/td&gt;
      &lt;td&gt;428029&lt;/td&gt;
      &lt;td&gt;515649&lt;/td&gt;
      &lt;td&gt;-1.56835&lt;/td&gt;
      &lt;td&gt;54.535351&lt;/td&gt;
      &lt;td&gt;1.974757e+08&lt;/td&gt;
      &lt;td&gt;107206.401694&lt;/td&gt;
      &lt;td&gt;POLYGON ((423496.602 524724.2984999996, 423497...&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create point dataset of the LSOA centrooids
# copy polygons to new GeoDataFrame
points = la.copy()
# change the geometry
points.geometry = points[&#39;geometry&#39;].centroid
# same crs
points.crs = la.crs
# lets reproject the data too
points = points.to_crs({&#39;init&#39;: &#39;epsg:4326&#39;})
# change projection
la = la.to_crs({&#39;init&#39;: &#39;epsg:4326&#39;})
# cut out unnecessary columns
points = points.loc[:,[&#39;objectid&#39;,&#39;lad17cd&#39;, &#39;lad17nm&#39;, &#39;geometry&#39;]]
points.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;/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;/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;/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;/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;/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;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Now we need to create lists of the values in the LA ID columns to&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# create list of unique LA id&#39;s from each dataset
x = list(df[&#39;OutLA&#39;].unique())
d = list(points[&#39;lad17cd&#39;].unique())
# chck if the number of LA codes matches 
len(x) - len(d)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;There is indeed some LA&#39;s that we don&#39;t have a spatial information for. Let&#39;s filter them out.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# lets get rid of the rows we dont have spatial information for, based on origins
df2 = df.loc[df[&#39;OutLA&#39;].isin(d)]
len(df) - len(df2)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;433719
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# Do the same thing for the destination
df3 = df2.loc[df2[&#39;InLA&#39;].isin(d)]
len(df) - len(df3)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;874611
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;According to this we have filtered out all together 874 thousand record that could not be found in the spatial dataset.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Let&#39;s write both datasets into a folder for further use.&lt;/p&gt;
&lt;h2 id=&#34;explore-rest-of-the-variables&#34;&gt;Explore rest of the variables&lt;/h2&gt;
&lt;p&gt;The main variable in the migration flows is the &amp;lsquo;Moves&amp;rsquo; which describes the intensity of the flow or the amount of people on the flow.&lt;/p&gt;
&lt;p&gt;From the historam below, we can see that most of the flows has a volume between 0 and circa 70. Thise are the less intensive flows, although the most frequent. However, little peak can be seen on number 800; high intensity flows that are less frequent.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;plt.figure(figsize=(9, 8))
sns.distplot(df3[&#39;Moves&#39;], color=&#39;g&#39;, bins=1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_39_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;The last variable is the age of the migrants. From the histogram below, it is clear that most of the migrating population is aged between 20 and 40. A little peak is also visible for the early age of 0 to 2 years old.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;df3[&#39;Age&#39;].hist(figsize=(8, 5), bins=50, xlabelsize=8, ylabelsize=8);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_41_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can we do histograms of age by each year?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The order of the years is as follows;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&#34;right&#34;&gt;2011&lt;/th&gt;
&lt;th align=&#34;right&#34;&gt;2012&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&#34;right&#34;&gt;2013&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;2014&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&#34;right&#34;&gt;2015&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;2016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&#34;right&#34;&gt;2017&lt;/td&gt;
&lt;td align=&#34;right&#34;&gt;2018&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;df4 = df3.reset_index()
# plot all the histograms alligned, with same y axe limit, to ensure we can compare them
f, axes = plt.subplots(4, 2, figsize=(15, 20), sharex=True)

sns.distplot( df4[&#39;Age&#39;].loc[df4[&#39;Date&#39;] == &#39;2011-01-01&#39;] , color=&amp;quot;skyblue&amp;quot;, label=&amp;quot;2011&amp;quot;, ax=axes[0, 0]).set_ylim(0,0.06)
sns.distplot( df4[&#39;Age&#39;].loc[df4[&#39;Date&#39;] == &#39;2012-01-01&#39;] , color=&amp;quot;blue&amp;quot;, label=&amp;quot;2012&amp;quot;, ax=axes[0, 1]).set_ylim(0,0.06)
sns.distplot( df4[&#39;Age&#39;].loc[df4[&#39;Date&#39;] == &#39;2013-01-01&#39;] , color=&amp;quot;purple&amp;quot;, label=&amp;quot;2013&amp;quot;, ax=axes[1, 0]).set_ylim(0,0.06)
sns.distplot( df4[&#39;Age&#39;].loc[df4[&#39;Date&#39;] == &#39;2014-01-01&#39;] , color=&amp;quot;darkred&amp;quot;, label=&amp;quot;2014&amp;quot;, ax=axes[1, 1]).set_ylim(0,0.06)
sns.distplot( df4[&#39;Age&#39;].loc[df4[&#39;Date&#39;] == &#39;2015-01-01&#39;] , color=&amp;quot;red&amp;quot;, label=&amp;quot;2015&amp;quot;, ax=axes[2, 0]).set_ylim(0,0.06)
sns.distplot( df4[&#39;Age&#39;].loc[df4[&#39;Date&#39;] == &#39;2016-01-01&#39;] , color=&amp;quot;chocolate&amp;quot;, label=&amp;quot;2016&amp;quot;, ax=axes[2, 1]).set_ylim(0,0.06)
sns.distplot( df4[&#39;Age&#39;].loc[df4[&#39;Date&#39;] == &#39;2017-01-01&#39;] , color=&amp;quot;darkorange&amp;quot;, label=&amp;quot;2017&amp;quot;, ax=axes[3, 0]).set_ylim(0,0.06)
sns.distplot( df4[&#39;Age&#39;].loc[df4[&#39;Date&#39;] == &#39;2018-01-01&#39;] , color=&amp;quot;gold&amp;quot;, label=&amp;quot;2018&amp;quot;, ax=axes[3, 1]).set_ylim(0,0.06)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;(0, 0.06)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_43_1.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Even here we can see that the 2011 have significantly different pattern from all the other years.&lt;/p&gt;
&lt;p&gt;To sum up this issue, we know that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the 2011 has more flows&lt;/li&gt;
&lt;li&gt;the 2011 has les volume of people on flows&lt;/li&gt;
&lt;li&gt;has very different pattern in both Age and gender&lt;/li&gt;
&lt;li&gt;there might have been changes in the methodology, but we cannot find exactt description of the changes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;This leads tho a decision to exclude the 2011 data in future analysis. Data with significantly different pattern without justified reasons would majorly affect the modelling and the results.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;## Maps of Origins and destinations
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# extract the long and lat from geometry into separate columns
points[&#39;lon&#39;] = points.geometry.x
points[&#39;lat&#39;] = points.geometry.y
&lt;/code&gt;&lt;/pre&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
outla = points.merge(df3, how = &#39;right&#39;, left_on = &#39;lad17cd&#39;, right_on = &#39;OutLA&#39;)
inla = points.merge(df3, how = &#39;right&#39;, left_on = &#39;lad17cd&#39;, right_on = &#39;InLA&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# Setup figure and axis
f, ax = plt.subplots(1, figsize=(9, 9))
# Add hexagon layer that displays count of points in each polygon
hb = ax.hexbin(outla.geometry.x, outla.geometry.y, gridsize=50, alpha=0.8, cmap=&#39;OrRd&#39;)
# Add title of the map
ax.set_title(&amp;quot;Hex-bin heatmap of UK internal migration - Leaving the Local Authority&amp;quot;)
# Add a colorbar (optional)
plt.colorbar(hb);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_48_0.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# Setup figure and axis
f, ax = plt.subplots(1, figsize=(9, 9))
# Add hexagon layer that displays count of points in each polygon
hb = ax.hexbin(inla.geometry.x, inla.geometry.y, gridsize=50, alpha=0.8, cmap=&#39;OrRd&#39;)
# Add title of the map
ax.set_title(&amp;quot;Hex-bin heatmap of UK internal migration - Moving to Local Authority&amp;quot;)
# Add a colorbar (optional)
plt.colorbar(hb);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;matplotlib.colorbar.Colorbar at 0x24c9186ccc8&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;migration/index_49_1.png&#34; alt=&#34;png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Although the heatmaps are quite easy maps to show, it is one of the worst choices one can make. You can read more about the heatmap issue &lt;a href=&#34;https://www.displayr.com/the-problem-with-heatmaps/&#34;&gt;here&lt;/a&gt;. In short, the issue is in the way our brains percieve the shading of the colors, this is also called the &lt;em&gt;checker shadow illusion&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In Part 2 I&#39;ll look more closely at the spatial structure using R and start some modelling.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# for that I just need to export the data I munged here, to use them later.
# df3.to_csv(index = False , path_or_buf = &#39;./../data/migration/munged/full_migration.csv&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# points.to_file(driver = &#39;ESRI Shapefile&#39;, filename = &#39;./../data/migration/munged/LAcentrooids.shp&#39;)
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
  </channel>
</rss>
