This Jupyter notebook contains analysis used to develop our guidance on findng the local authority for an address.

The software is open source, and published under an MIT licence. The data in the spatialite database used for this analysis was built using OS AddressBase, combined with open data sources used under the licenses identified in the README and Makefile.

In [5]:
# load spatialte database
import os
import sqlite3

conn = sqlite3.connect("addresses.db")
conn.enable_load_extension(True)
conn.load_extension(os.environ.get("SPATIALITE_EXTENSION", "/usr/lib/x86_64-linux-gnu/mod_spatialite.so"))
c = conn.cursor()
In [3]:
# number of UPRNs
c.execute("SELECT count(*) FROM uprn")
uprn_total = c.fetchone()[0]
print(f"{uprn_total:,}")
39,095,248
In [4]:
# number of postcodes
c.execute("SELECT count(*) FROM postcode")
postcode_total = c.fetchone()[0]
print(f"{postcode_total:,}")
2,643,728
In [5]:
# postcodes with the most number of UPRNs ..
c.execute("""
SELECT postcode, count(*)
FROM uprn
GROUP BY postcode
ORDER BY count(*) DESC
LIMIT 50
""")
c.fetchall()
Out[5]:
[('CV4 7ES', 6276),
 ('NR4 7TJ', 4725),
 ('CO4 3SQ', 3303),
 ('SA1 8EP', 2044),
 ('HG3 5JL', 1570),
 ('PO20 9BH', 1546),
 ('CF14 3UU', 1496),
 ('YO25 8SY', 1478),
 ('DL8 4AT', 1476),
 ('CF37 1DL', 1468),
 ('LE11 3TZ', 1442),
 ('LS16 5PT', 1287),
 ('BN1 9BJ', 1280),
 ('CV4 7AL', 1273),
 ('L3 5UE', 1268),
 ('UB8 3PH', 1216),
 ('CF14 3UX', 1164),
 ('L6 1AH', 1159),
 ('YO15 3QN', 1151),
 ('BN1 9RJ', 1148),
 ('LS2 8PD', 1146),
 ('PE25 1LX', 1142),
 ('M14 6FZ', 1123),
 ('WC1H 9EN', 1106),
 ('BN1 9RP', 1087),
 ('IV27 4NZ', 1066),
 ('AL10 9UF', 1043),
 ('E14 9GE', 1034),
 ('E14 7TZ', 1020),
 ('NN1 5PH', 1011),
 ('OX3 0BD', 999),
 ('LS2 9EL', 988),
 ('CW1 5NP', 977),
 ('AL10 9UZ', 977),
 ('L3 5GA', 965),
 ('B4 7XG', 950),
 ('E1 7AB', 934),
 ('SW17 0PZ', 933),
 ('LS18 5HD', 932),
 ('LN4 4LR', 925),
 ('CT2 7BQ', 909),
 ('HU6 7EL', 908),
 ('LS2 8BY', 907),
 ('G3 8PX', 895),
 ('L6 1BA', 891),
 ('CO2 8GY', 891),
 ('SS8 0DB', 877),
 ('KT6 5PJ', 864),
 ('WC1H 0AQ', 863),
 ('E15 2DA', 863)]
In [27]:
# number of UPRNs where ONSUD and ONSPD differ
c.execute("""
SELECT
  COUNT(*)
  FROM uprn
  INNER JOIN postcode
  ON postcode.postcode = uprn.postcode
  WHERE uprn.onsud != postcode.onspd
""")
onsud_onspd_differ = c.fetchone()[0]
print(f"{onsud_onspd_differ:,}")
393,693
In [28]:
# percentage of GB UPRNs where ONSUD and ONSPD differ
print("%2.1f%%" % (onsud_onspd_differ / uprn_total * 100))
1.0%
In [20]:
# number of UPRNs in an English ONSPD postcode
c.execute("""
SELECT
  COUNT(*)
  FROM uprn
  INNER JOIN postcode
  ON postcode.postcode = uprn.postcode
  WHERE substr(onsud, 1, 1) == 'E'
""")
england_uprn_total = c.fetchone()[0]
print(f"{england_uprn_total:,}")
33,427,350
In [23]:
# number of UPRNs in an English ONSPD postcode where ONSUD and ONSPD differ
c.execute("""
SELECT
  COUNT(*)
  FROM uprn
  INNER JOIN postcode
  ON postcode.postcode = uprn.postcode
  WHERE substr(uprn.onsud, 1, 1) == 'E'
    AND uprn.onsud != postcode.onspd
""")
england_onsud_onspd_differ = c.fetchone()[0]
print(f"{england_onsud_onspd_differ:,}")
386,103
In [25]:
# percentage of English UPRNs where ONSUD and ONSPD differ
print("%2.1f%%" % (england_onsud_onspd_differ / england_uprn_total * 100))
1.2%
In [10]:
%config InlineBackend.figure_formats = ['svg']
import matplotlib.pyplot as plt
%matplotlib inline
from numpy import array
In [11]:
# postcodes with UPRNs which are attributed to different LAs by ONSUD
c.execute("""
SELECT COUNT(DISTINCT onsud) count, postcode
  FROM uprn
  GROUP BY postcode
  ORDER BY count DESC
""")

def dict_from_fetch(l):
    d = dict()
    for a, b in l:
        d.setdefault(a, [])
        d[a].append(b)
    return d

postcode_lad_count = dict_from_fetch(c.fetchall())
postcode_lad_counts = array([(n, len(postcode_lad_count[n])) for n in sorted(postcode_lad_count)])
print(postcode_lad_counts)
[[      1 1698266]
 [      2   15992]
 [      3     227]
 [      4       1]]
In [12]:
fig, ax = plt.subplots()
data = array(postcode_lad_counts)
ax.set_title('Local authority districts by postcode')
ax.set_xlabel("Number of different districts")
ax.set_ylabel("Number of postcodes")
x = data[:, :-1].flatten()
y = data[:, -1]
plt.xticks( x )
ax.ticklabel_format(style='plain')
p = plt.bar(x, y)
2020-08-03T16:39:05.174143 image/svg+xml Matplotlib v3.3.0, https://matplotlib.org/
In [13]:
# postcodes with 3 or 4 multiple local authority districts
print([(n, postcode_lad_count[n]) for n in sorted(postcode_lad_count, reverse=True) if n > 2])
[(4, ['BN15 0AY']), (3, ['YO32 5TP', 'YO23 3PA', 'WV15 6HS', 'WV14 8TU', 'WV14 8NH', 'WS15 3NW', 'WS12 4PT', 'WR5 3HR', 'WF8 3JF', 'WF2 0SB', 'WD3 9YD', 'WD25 8PZ', 'WD19 4BE', 'WC2A 2LL', 'WA4 4EB', 'WA3 5LH', 'UB5 4DT', 'TS8 9DY', 'TS21 1LX', 'TS2 1UB', 'TQ9 6QB', 'TQ3 1SY', 'TN3 8JH', 'TN27 9JE', 'TN27 8LD', 'TN25 5JF', 'TN16 2NS', 'TF9 2SH', 'TF10 8DS', 'TA7 9BN', 'TA20 3RU', 'SY20 9LD', 'SY13 4HB', 'SW15 3QF', 'SW15 1LB', 'ST7 3PX', 'ST7 3PG', 'ST3 6HJ', 'ST10 4PF', 'SS8 0PY', 'SS8 0PS', 'SS6 7UA', 'SS11 8SG', 'SP5 5RA', 'SO40 3QS', 'SO16 3DQ', 'SN8 2JX', 'SN6 6JZ', 'SL6 0JA', 'SK17 0TG', 'SG9 0BH', 'SG7 5JH', 'SE12 9EZ', 'SA9 2XH', 'SA8 4RU', 'S80 3DZ', 'S6 6GL', 'S44 6AE', 'S36 4HH', 'S21 3UB', 'S17 3BB', 'S10 4QZ', 'RM14 3PB', 'RH6 9ST', 'RH6 0EL', 'RH5 6NS', 'RH19 3PR', 'RH12 3BE', 'RH12 3AP', 'RG7 2BG', 'RG2 6GF', 'RG2 6AT', 'PR4 1UN', 'PR3 1UT', 'PO16 9DR', 'PL20 6SG', 'PL15 9QX', 'PH22 1RB', 'PH15 2PX', 'PE6 7QB', 'PE13 5RF', 'PA36 4AG', 'OX29 4DE', 'OX15 5BX', 'OL3 5UN', 'NW2 3EJ', 'NW10 5NU', 'NR9 5DH', 'NR6 5AY', 'NR29 3BW', 'NR13 5HQ', 'NR13 5HH', 'NP7 7NW', 'NP7 0PU', 'NP44 7AS', 'NP22 3AF', 'NP11 5BG', 'NN9 5HW', 'NN3 9BZ', 'NN3 5GP', 'NN29 7NP', 'NN14 1EB', 'NN13 5JF', 'NG6 8WN', 'NG6 8SS', 'NG23 5FF', 'NG22 9EY', 'NG19 7QB', 'NG16 3JE', 'NG13 9PB', 'NG10 2FZ', 'NE15 8QE', 'N19 5JF', 'MK43 0AT', 'MK17 9HD', 'ME9 7UY', 'ME5 9AX', 'M9 7HW', 'M5 4TH', 'M34 2NF', 'M26 1EW', 'M22 5YA', 'LU2 8PE', 'LN8 6BW', 'LN6 4RY', 'LE7 9SJ', 'LE7 9DL', 'LE67 9QE', 'LE2 9RQ', 'LE2 2FB', 'LE18 3TH', 'LE16 8RT', 'LE15 8DS', 'LE10 3AR', 'LD7 1LU', 'LA6 3PH', 'LA22 9JU', 'LA2 9DW', 'L35 6PG', 'KT6 4ES', 'KT23 3HY', 'KT22 0DN', 'KT18 7TR', 'KA6 7QE', 'KA30 8SN', 'IP8 4BB', 'IP27 0QF', 'IP22 1SH', 'HR8 1SE', 'HP27 9PN', 'HA5 3YA', 'GU9 0LZ', 'GU24 9BA', 'GU15 3DT', 'GU10 5EB', 'GL7 3HA', 'GL56 0ST', 'GL53 9QQ', 'GL3 4TT', 'GL2 3NW', 'GL19 4NE', 'GL19 4HE', 'GL16 8NZ', 'GL13 9QY', 'G76 9BJ', 'G64 1UR', 'G63 0JS', 'FK21 8UB', 'EX18 7BG', 'EX16 9JS', 'EX14 9RF', 'EN7 5JB', 'EN11 0RF', 'EH36 5PN', 'E7 0EF', 'E1 6DB', 'DY14 9DP', 'DY10 4RB', 'DN9 3NT', 'DN40 3AD', 'DL8 3LX', 'DL2 1AX', 'DL11 6ED', 'DG8 6TB', 'DG14 0RU', 'DE65 5PN', 'DE6 5BJ', 'DE6 3BU', 'DE55 7RH', 'DE21 7HW', 'DD2 5LD', 'DA3 8LD', 'DA13 0XF', 'CW9 6NU', 'CW3 9QW', 'CV9 3ER', 'CV4 8AP', 'CV37 8AQ', 'CV37 8AG', 'CV23 0AQ', 'CV10 0TT', 'CR5 2PQ', 'CO5 9BJ', 'CM77 8QW', 'CF83 1NG', 'CF72 8NU', 'CF48 2HY', 'CF37 4HP', 'CF35 5HY', 'CF32 8AH', 'CF3 6LP', 'BS13 0NR', 'BL9 6UW', 'BL8 4JS', 'BL6 6SL', 'BD23 3JT', 'BD23 3JR', 'BB2 7QA', 'BB12 9AR', 'BB1 4AG', 'B94 5NH', 'B94 5JT', 'B79 0BT', 'B79 0BH', 'B47 6BJ', 'AL6 9BY', 'AB35 5XU'])]
In [78]:
# postcodes with UPRNs attributed to 3 or more LAs by ONSUD
c.execute("""
SELECT postcode, lad_count, uprn_count
FROM (SELECT postcode, COUNT(DISTINCT onsud) lad_count, COUNT(uprn) uprn_count
  FROM uprn
   GROUP BY postcode
    ORDER BY lad_count DESC)
     WHERE lad_count > 2
""")
postcodes = c.fetchall()
print(postcodes)
[('BN15 0AY', 4, 43), ('YO32 5TP', 3, 67), ('YO23 3PA', 3, 56), ('WV15 6HS', 3, 25), ('WV14 8TU', 3, 65), ('WV14 8NH', 3, 10), ('WS15 3NW', 3, 19), ('WS12 4PT', 3, 16), ('WR5 3HR', 3, 55), ('WF8 3JF', 3, 37), ('WF2 0SB', 3, 18), ('WD3 9YD', 3, 12), ('WD25 8PZ', 3, 20), ('WD19 4BE', 3, 25), ('WC2A 2LL', 3, 11), ('WA4 4EB', 3, 8), ('WA3 5LH', 3, 11), ('UB5 4DT', 3, 83), ('TS8 9DY', 3, 20), ('TS21 1LX', 3, 24), ('TS2 1UB', 3, 569), ('TQ9 6QB', 3, 31), ('TQ3 1SY', 3, 19), ('TN3 8JH', 3, 28), ('TN27 9JE', 3, 32), ('TN27 8LD', 3, 42), ('TN25 5JF', 3, 11), ('TN16 2NS', 3, 12), ('TF9 2SH', 3, 27), ('TF10 8DS', 3, 24), ('TA7 9BN', 3, 23), ('TA20 3RU', 3, 16), ('SY20 9LD', 3, 57), ('SY13 4HB', 3, 30), ('SW15 3QF', 3, 17), ('SW15 1LB', 3, 65), ('ST7 3PX', 3, 11), ('ST7 3PG', 3, 4), ('ST3 6HJ', 3, 25), ('ST10 4PF', 3, 35), ('SS8 0PY', 3, 172), ('SS8 0PS', 3, 69), ('SS6 7UA', 3, 36), ('SS11 8SG', 3, 16), ('SP5 5RA', 3, 18), ('SO40 3QS', 3, 38), ('SO16 3DQ', 3, 21), ('SN8 2JX', 3, 21), ('SN6 6JZ', 3, 51), ('SL6 0JA', 3, 26), ('SK17 0TG', 3, 17), ('SG9 0BH', 3, 20), ('SG7 5JH', 3, 7), ('SE12 9EZ', 3, 29), ('SA9 2XH', 3, 12), ('SA8 4RU', 3, 51), ('S80 3DZ', 3, 99), ('S6 6GL', 3, 60), ('S44 6AE', 3, 7), ('S36 4HH', 3, 57), ('S21 3UB', 3, 13), ('S17 3BB', 3, 29), ('S10 4QZ', 3, 65), ('RM14 3PB', 3, 48), ('RH6 9ST', 3, 63), ('RH6 0EL', 3, 74), ('RH5 6NS', 3, 24), ('RH19 3PR', 3, 20), ('RH12 3BE', 3, 21), ('RH12 3AP', 3, 13), ('RG7 2BG', 3, 11), ('RG2 6GF', 3, 12), ('RG2 6AT', 3, 37), ('PR4 1UN', 3, 54), ('PR3 1UT', 3, 18), ('PO16 9DR', 3, 90), ('PL20 6SG', 3, 23), ('PL15 9QX', 3, 35), ('PH22 1RB', 3, 147), ('PH15 2PX', 3, 177), ('PE6 7QB', 3, 26), ('PE13 5RF', 3, 76), ('PA36 4AG', 3, 149), ('OX29 4DE', 3, 22), ('OX15 5BX', 3, 32), ('OL3 5UN', 3, 91), ('NW2 3EJ', 3, 35), ('NW10 5NU', 3, 50), ('NR9 5DH', 3, 21), ('NR6 5AY', 3, 15), ('NR29 3BW', 3, 32), ('NR13 5HQ', 3, 58), ('NR13 5HH', 3, 8), ('NP7 7NW', 3, 31), ('NP7 0PU', 3, 42), ('NP44 7AS', 3, 34), ('NP22 3AF', 3, 11), ('NP11 5BG', 3, 26), ('NN9 5HW', 3, 32), ('NN3 9BZ', 3, 31), ('NN3 5GP', 3, 55), ('NN29 7NP', 3, 37), ('NN14 1EB', 3, 17), ('NN13 5JF', 3, 27), ('NG6 8WN', 3, 21), ('NG6 8SS', 3, 21), ('NG23 5FF', 3, 73), ('NG22 9EY', 3, 11), ('NG19 7QB', 3, 15), ('NG16 3JE', 3, 28), ('NG13 9PB', 3, 46), ('NG10 2FZ', 3, 37), ('NE15 8QE', 3, 33), ('N19 5JF', 3, 8), ('MK43 0AT', 3, 19), ('MK17 9HD', 3, 40), ('ME9 7UY', 3, 22), ('ME5 9AX', 3, 8), ('M9 7HW', 3, 46), ('M5 4TH', 3, 49), ('M34 2NF', 3, 12), ('M26 1EW', 3, 14), ('M22 5YA', 3, 447), ('LU2 8PE', 3, 34), ('LN8 6BW', 3, 25), ('LN6 4RY', 3, 35), ('LE7 9SJ', 3, 35), ('LE7 9DL', 3, 30), ('LE67 9QE', 3, 33), ('LE2 9RQ', 3, 56), ('LE2 2FB', 3, 40), ('LE18 3TH', 3, 23), ('LE16 8RT', 3, 19), ('LE15 8DS', 3, 57), ('LE10 3AR', 3, 22), ('LD7 1LU', 3, 35), ('LA6 3PH', 3, 16), ('LA22 9JU', 3, 97), ('LA2 9DW', 3, 23), ('L35 6PG', 3, 40), ('KT6 4ES', 3, 25), ('KT23 3HY', 3, 11), ('KT22 0DN', 3, 11), ('KT18 7TR', 3, 77), ('KA6 7QE', 3, 70), ('KA30 8SN', 3, 58), ('IP8 4BB', 3, 12), ('IP27 0QF', 3, 14), ('IP22 1SH', 3, 27), ('HR8 1SE', 3, 36), ('HP27 9PN', 3, 67), ('HA5 3YA', 3, 19), ('GU9 0LZ', 3, 71), ('GU24 9BA', 3, 22), ('GU15 3DT', 3, 55), ('GU10 5EB', 3, 32), ('GL7 3HA', 3, 10), ('GL56 0ST', 3, 18), ('GL53 9QQ', 3, 42), ('GL3 4TT', 3, 36), ('GL2 3NW', 3, 33), ('GL19 4NE', 3, 19), ('GL19 4HE', 3, 18), ('GL16 8NZ', 3, 18), ('GL13 9QY', 3, 29), ('G76 9BJ', 3, 11), ('G64 1UR', 3, 17), ('G63 0JS', 3, 46), ('FK21 8UB', 3, 151), ('EX18 7BG', 3, 11), ('EX16 9JS', 3, 14), ('EX14 9RF', 3, 59), ('EN7 5JB', 3, 15), ('EN11 0RF', 3, 36), ('EH36 5PN', 3, 33), ('E7 0EF', 3, 31), ('E1 6DB', 3, 50), ('DY14 9DP', 3, 10), ('DY10 4RB', 3, 20), ('DN9 3NT', 3, 77), ('DN40 3AD', 3, 21), ('DL8 3LX', 3, 76), ('DL2 1AX', 3, 31), ('DL11 6ED', 3, 5), ('DG8 6TB', 3, 75), ('DG14 0RU', 3, 14), ('DE65 5PN', 3, 149), ('DE6 5BJ', 3, 178), ('DE6 3BU', 3, 84), ('DE55 7RH', 3, 30), ('DE21 7HW', 3, 118), ('DD2 5LD', 3, 9), ('DA3 8LD', 3, 22), ('DA13 0XF', 3, 27), ('CW9 6NU', 3, 23), ('CW3 9QW', 3, 28), ('CV9 3ER', 3, 32), ('CV4 8AP', 3, 54), ('CV37 8AQ', 3, 11), ('CV37 8AG', 3, 25), ('CV23 0AQ', 3, 42), ('CV10 0TT', 3, 26), ('CR5 2PQ', 3, 31), ('CO5 9BJ', 3, 44), ('CM77 8QW', 3, 8), ('CF83 1NG', 3, 65), ('CF72 8NU', 3, 56), ('CF48 2HY', 3, 42), ('CF37 4HP', 3, 31), ('CF35 5HY', 3, 116), ('CF32 8AH', 3, 77), ('CF3 6LP', 3, 48), ('BS13 0NR', 3, 12), ('BL9 6UW', 3, 20), ('BL8 4JS', 3, 36), ('BL6 6SL', 3, 23), ('BD23 3JT', 3, 56), ('BD23 3JR', 3, 14), ('BB2 7QA', 3, 69), ('BB12 9AR', 3, 8), ('BB1 4AG', 3, 17), ('B94 5NH', 3, 26), ('B94 5JT', 3, 11), ('B79 0BT', 3, 17), ('B79 0BH', 3, 17), ('B47 6BJ', 3, 7), ('AL6 9BY', 3, 12), ('AB35 5XU', 3, 43)]
In [83]:
from ipyleaflet import Map, Marker, MarkerCluster, GeoJSON
import json

def postcode_map(postcode):
    c.execute("""
        SELECT uprn, X(point), Y(point), onsud, geography.name 
          FROM uprn 
          INNER JOIN geography
          ON geography.geography == uprn.onsud
          WHERE uprn.postcode == "%s"
          """ % (postcode))
    clusters = {}
    lads = {}
    for p in c.fetchall():
        lad = p[3]
        lads[lad] = 1
        clusters.setdefault(lad, [])
        clusters[lad].append(Marker(location=(p[2], p[1]), title="%s %s (%s)" % (p[0], p[4], p[3])))
        
    m = Map(center=(50.8532019, -0.3528908), zoom=4)
    for cluster in clusters:
        m.add_layer(MarkerCluster(markers=clusters[cluster]));
    for lad in lads:
        c.execute('SELECT AsGeoJSON(geometry) FROM geography WHERE geography == "%s"' % (lad))
        geo_json = GeoJSON(data=json.loads(c.fetchone()[0]), style={'fillOpacity': 0}) 
        m.add_layer(geo_json)
    return m
In [80]:
postcode_map("BN15 0AY")
In [78]:
postcode_map("CV4 7ES")
In [84]:
postcode_map("LA22 9JU")
In [70]:
# postcodes where ONSUD UPRNs span multiple nations
c.execute("""
SELECT postcode
FROM (SELECT
    postcode,  COUNT(DISTINCT substr(onsud, 1, 1)) count
    FROM uprn
    GROUP BY postcode
    ORDER BY count DESC)
  WHERE count > 1
""")
postcodes = list(array(c.fetchall()).flatten())
print(len(postcodes))
print(postcodes)
286
['TD9 0TW', 'TD9 0TU', 'TD9 0SR', 'TD9 0SL', 'TD8 6PS', 'TD8 6NL', 'TD8 6NJ', 'TD5 8QA', 'TD5 8PT', 'TD5 8HT', 'TD5 8DB', 'TD5 8DA', 'TD5 8BZ', 'TD5 8BP', 'TD5 8AR', 'TD15 2XN', 'TD15 1XB', 'TD15 1UZ', 'TD15 1UY', 'TD15 1UT', 'TD15 1UF', 'TD15 1SZ', 'TD15 1SU', 'TD12 4RS', 'TD12 4RR', 'TD12 4NT', 'TD12 4LF', 'TD12 4AT', 'SY9 5JW', 'SY9 5JR', 'SY9 5JP', 'SY9 5JN', 'SY9 5JL', 'SY9 5HQ', 'SY7 8QX', 'SY7 8PR', 'SY7 8PP', 'SY7 0BA', 'SY5 9RZ', 'SY5 9PE', 'SY5 9DP', 'SY5 9BW', 'SY5 9AW', 'SY5 9AT', 'SY5 9AN', 'SY5 0JW', 'SY5 0JP', 'SY5 0JN', 'SY5 0JL', 'SY5 0JJ', 'SY5 0JH', 'SY4 5NN', 'SY22 6TW', 'SY22 6TN', 'SY22 6SH', 'SY22 6QY', 'SY22 6LQ', 'SY22 6LN', 'SY22 6LH', 'SY22 6JZ', 'SY22 6JY', 'SY22 6HB', 'SY22 6EL', 'SY22 6EJ', 'SY22 6EA', 'SY22 6BA', 'SY21 8JZ', 'SY21 8JY', 'SY21 8JL', 'SY21 8JB', 'SY21 8JA', 'SY21 8ES', 'SY21 8ER', 'SY21 8EN', 'SY21 8EG', 'SY15 6UL', 'SY15 6UB', 'SY15 6TZ', 'SY15 6TY', 'SY15 6TW', 'SY15 6TR', 'SY15 6SY', 'SY15 6SR', 'SY15 6SP', 'SY15 6HZ', 'SY15 6HY', 'SY15 6EB', 'SY15 6DS', 'SY15 6DF', 'SY15 6BE', 'SY15 6AU', 'SY15 6AT', 'SY14 7NG', 'SY14 7NE', 'SY14 7LU', 'SY14 7LN', 'SY14 7JX', 'SY14 7JR', 'SY14 7BT', 'SY14 7BN', 'SY14 7AZ', 'SY14 7AW', 'SY14 7AN', 'SY13 4RE', 'SY13 3NZ', 'SY13 3NJ', 'SY13 3DL', 'SY13 2RY', 'SY13 2PB', 'SY13 2PA', 'SY13 2LT', 'SY13 2LJ', 'SY13 2LH', 'SY13 2LG', 'SY13 2LF', 'SY13 2JZ', 'SY12 0QB', 'SY12 0NQ', 'SY12 0NL', 'SY12 0NG', 'SY12 0LS', 'SY11 3EE', 'SY10 9JQ', 'SY10 9JG', 'SY10 9BW', 'SY10 9BU', 'SY10 9BS', 'SY10 8LB', 'SY10 7PU', 'SY10 7PS', 'SY10 7PD', 'SY10 7PB', 'SY10 7NT', 'SY10 7NR', 'SY10 7NP', 'SY10 7ND', 'SY10 7JF', 'SY10 7HU', 'NP7 8UH', 'NP7 8HE', 'NP7 8HD', 'NP7 8EB', 'NP7 7PH', 'NP7 7PG', 'NP7 7NW', 'NP7 7NN', 'NP26 5TD', 'NP25 5RZ', 'NP25 5RL', 'NP25 5RJ', 'NP25 5RD', 'NP25 5QQ', 'NP25 4LY', 'NP25 4LU', 'NP25 4LN', 'NP25 4AN', 'NP25 3SS', 'NP25 3SR', 'NP16 7PE', 'NP16 7NG', 'NP16 7JD', 'NP16 7HG', 'NP16 7EQ', 'NP16 7DL', 'NP16 6SF', 'NP16 5PB', 'NE65 7DD', 'LL14 5DN', 'LL14 5BU', 'LL14 5BL', 'LL14 5BH', 'LL13 9YS', 'LL13 9US', 'LL13 0LG', 'LL12 0DF', 'LL12 0BY', 'LL12 0BU', 'LE67 3FQ', 'LD8 2SH', 'LD8 2PT', 'LD8 2PS', 'LD8 2PR', 'LD8 2PP', 'LD8 2ND', 'LD8 2LE', 'LD8 2LD', 'LD8 2HN', 'LD8 2HL', 'LD8 2HH', 'LD8 2HD', 'LD8 2HB', 'LD8 2EU', 'LD8 2AG', 'LD7 1YU', 'LD7 1YT', 'LD7 1YS', 'LD7 1YG', 'LD7 1YD', 'LD7 1UP', 'LD7 1TU', 'LD7 1TT', 'LD7 1NA', 'LD7 1LY', 'LD7 1LU', 'LD7 1LT', 'LD7 1EE', 'LD7 1DT', 'LD7 1AG', 'KY16 8JW', 'HR5 3QD', 'HR5 3QA', 'HR5 3PH', 'HR5 3PE', 'HR5 3PA', 'HR5 3NZ', 'HR5 3NW', 'HR5 3EW', 'HR5 3EP', 'HR3 6JW', 'HR3 6JH', 'HR3 6HW', 'HR3 6HL', 'HR3 6HJ', 'HR3 6HA', 'HR3 6EU', 'HR3 5RQ', 'HR3 5RE', 'HR3 5QZ', 'HR3 5BG', 'HR3 5BA', 'HR3 5AR', 'HR2 8RA', 'HR2 0PX', 'HR2 0PN', 'HR2 0HE', 'HR2 0DA', 'HR2 0BY', 'GL16 8PB', 'GL16 8NZ', 'GL15 6RT', 'GL15 6QG', 'DG16 5JD', 'DG16 5GD', 'DG16 5EU', 'DG14 0TR', 'DG14 0TF', 'DG14 0RU', 'DG14 0RS', 'DG14 0RE', 'CH7 4EF', 'CH64 5SP', 'CH64 5SF', 'CH64 5SB', 'CH61 0HN', 'CH6 5XA', 'CH5 2LH', 'CH4 9LN', 'CH4 9DS', 'CH4 9AQ', 'CH4 9AE', 'CH4 8UB', 'CH4 8SE', 'CH4 8RL', 'CH4 8RH', 'CH4 8QT', 'CH4 8QF', 'CH4 8PY', 'CH4 8NH', 'CH4 8LW', 'CH4 8LS', 'CH4 8LN', 'CH4 8GA', 'CH4 0RU', 'CH4 0DX', 'CH4 0DF', 'CH1 6HU', 'CH1 6BS', 'CH1 6BJ', 'CH1 6AG', 'CH1 6AF', 'CH1 4QX', 'CH1 4QJ', 'CH1 4LT', 'CH1 4LQ', 'CF37 2BW', 'BS21 7UF']
In [47]:
# number of UPRNs which ONSUD says are in England, but the ONSPD LAD is in another nation
c.execute("""
SELECT COUNT(*) FROM uprn
  INNER JOIN postcode
  ON postcode.postcode = uprn.postcode
  WHERE substr(uprn.onsud, 1, 1) != 'E' 
  AND substr(postcode.onspd, 1, 1) == 'E'
""")
c.fetchone()[0]
Out[47]:
609
In [48]:
# number of UPRNs which the ONSPD LAD is in England, but ONSUD says are in another nation 
c.execute("""
SELECT COUNT(*) FROM uprn
  INNER JOIN postcode
  ON postcode.postcode = uprn.postcode
  WHERE substr(uprn.onsud, 1, 1) == 'E' 
  AND substr(postcode.onspd, 1, 1) != 'E'
""")
c.fetchone()[0]
Out[48]:
609
In [77]:
c.execute("""
SELECT postcode FROM postcode
WHERE postcode.nspl != postcode.onspd
""")
postcodes = list(array(c.fetchall()).flatten())
print(len(postcodes))
print(postcodes)
215
['AL4 0QL', 'BL9 8RH', 'BT17 0BU', 'BT17 0EZ', 'BT17 0FE', 'BT17 0LL', 'BT17 0LP', 'BT17 0LR', 'BT17 0LW', 'BT17 0NS', 'BT17 0NT', 'BT17 0QL', 'BT17 0YU', 'BT25 2DB', 'BT25 2DD', 'BT25 2DZ', 'BT25 2EA', 'BT25 2EE', 'BT25 2EG', 'BT25 2EQ', 'BT25 2EW', 'BT25 2HJ', 'BT25 2HL', 'BT25 2HQ', 'BT25 2HR', 'BT25 2HW', 'BT25 2LG', 'BT29 4LH', 'BT31 9PT', 'BT31 9PX', 'BT31 9PZ', 'BT31 9QD', 'BT31 9QE', 'BT31 9QX', 'BT31 9QY', 'BT31 9QZ', 'BT31 9RE', 'BT31 9RS', 'BT31 9SA', 'BT31 9SB', 'BT32 5HD', 'BT38 8BA', 'BT38 8UY', 'BT38 8XA', 'BT38 8YG', 'BT38 8ZQ', 'BT38 8ZR', 'BT38 8ZS', 'BT5 7QD', 'BT5 7QZ', 'BT5 7ST', 'BT5 7SU', 'BT5 7SX', 'BT5 7TA', 'BT5 7TS', 'BT5 7TT', 'BT5 7TX', 'BT5 7TZ', 'BT6 0NJ', 'BT6 0PA', 'BT67 0UA', 'BT71 6LX', 'BT71 6LY', 'BT71 6LZ', 'BT71 6NR', 'BT75 0NB', 'BT8 4AB', 'BT8 4AR', 'BT8 4AX', 'BT8 4BA', 'BT8 4BB', 'BT8 4BD', 'BT8 4BE', 'BT8 4BF', 'BT8 4BS', 'BT8 4DD', 'BT8 4EB', 'BT8 4ET', 'BT8 4EU', 'BT8 4EW', 'BT8 4EY', 'BT8 4EZ', 'BT8 4FJ', 'BT8 4FL', 'BT8 4FY', 'BT8 4FZ', 'BT8 4RE', 'BT8 4RF', 'BT8 4TA', 'BT8 4TB', 'BT8 4TJ', 'BT8 4TL', 'BT8 4TR', 'BT8 4TT', 'BT8 4TZ', 'BT8 4XJ', 'BT8 4ZE', 'BT8 4ZF', 'BT8 4ZH', 'BT8 4ZJ', 'BT8 4ZL', 'BT8 4ZQ', 'BT8 4ZS', 'BT8 4ZT', 'BT8 4ZU', 'BT8 4ZX', 'BT8 4ZY', 'BT8 6AB', 'BT8 6AT', 'BT8 6AX', 'BT8 6FX', 'BT8 6FY', 'BT8 6FZ', 'BT8 6RB', 'BT8 6TB', 'BT8 7AF', 'BT8 7AH', 'BT8 7AS', 'BT8 7BA', 'BT8 7BB', 'BT8 7BD', 'BT8 7BE', 'BT8 7BS', 'BT8 7DD', 'BT8 7EB', 'BT8 7ET', 'BT8 7EU', 'BT8 7EW', 'BT8 7EY', 'BT8 7EZ', 'BT8 7FL', 'BT8 7QD', 'BT8 7RE', 'BT8 7RF', 'BT8 7TR', 'BT8 7XH', 'BT8 7XJ', 'BT8 8JT', 'BT8 8JU', 'BT8 8JX', 'BT8 8JZ', 'BT8 8LH', 'CR2 3PJ', 'CR3 0EA', 'DN6 7EZ', 'E18 2LH', 'E18 2LQ', 'EC1R 4LS', 'EC1R 4ST', 'EC1R 4TU', 'EC1R 4UA', 'EC1R 5HE', 'G33 6GS', 'G33 6GT', 'G33 6GU', 'G33 6GW', 'G33 6GX', 'G33 6GY', 'G33 6GZ', 'G33 6NS', 'GL19 3AZ', 'HA0 1EA', 'M15 4BS', 'M35 6BB', 'M35 6BD', 'M43 6TD', 'M7 4ZJ', 'M8 6EJ', 'M8 7BU', 'M8 7FE', 'M8 9YH', 'N11 2DR', 'N15 6BG', 'N15 6BQ', 'N15 6PZ', 'N8 9RD', 'NE43 7SX', 'NG19 0LN', 'NP26 3BD', 'NP3 2LP', 'NW10 5NU', 'NW1 4SY', 'OL11 2BZ', 'RG2 8HD', 'RM8 1RQ', 'RM8 1XN', 'S35 4LG', 'SE3 0SR', 'SE9 4HD', 'SK4 5EZ', 'SK5 6QT', 'SM4 6SG', 'SP6 3DD', 'SW1W 8RD', 'SW1W 8RE', 'SW1W 8RL', 'SW1X 7XF', 'SW1X 7XG', 'SW1X 7XJ', 'SW1X 7XU', 'SW1X 7XY', 'SW3 1EH', 'SW3 1EJ', 'SW3 1EL', 'SW3 1EQ', 'SW3 1EU', 'SW3 1EW', 'SW3 1EZ', 'SW3 1HB', 'SW3 1HE', 'TN16 3UP', 'TW8 9LX', 'W12 9DG', 'W5 4BN', 'WF17 0AF']
In [49]:
# number of UPRNs where the ONSPD LAD differs to the NSPL LAD 
c.execute("""
SELECT COUNT(*) FROM uprn
INNER JOIN postcode
  ON postcode.postcode = uprn.postcode
  WHERE postcode.nspl != postcode.onspd
""")
c.fetchone()[0]
Out[49]:
543
In [65]:
# postcodes where the OS codepo LAD differes to the ONSPD LAD 
c.execute("""
SELECT COUNT(*) FROM postcode
  WHERE postcode.codepo != postcode.onspd
""")
print('{:,}'.format(c.fetchone()[0]))
927,371
In [66]:
# number of UPRNs where the OS codepo LAD differs to the ONSPD LAD 
c.execute("""
SELECT COUNT(*) FROM uprn
INNER JOIN postcode
  ON postcode.postcode = uprn.postcode
  WHERE postcode.codepo != postcode.onspd
""")
print('{:,}'.format(c.fetchone()[0]))
155,529