UK 2015 Election Mapped

Just providing a quick update to the previous post. Since that was done a few weeks ago, Evan Odell has been doing some great work on enhancing his Hansard package details of which you can view here.

I plan to do some more work on this incredible resource in the future but for now am just looking at some mapping. This entails using the sf package for the first time - one which the experts are saying is going to have big impact on R development and a spot more purrr (which is part of the tidyverse environment)

First we will download the packages

library(hansard)
library(sf)
library(leaflet)
library(stringr)
library(tidyverse)

The hansard package provides information on constituency data including names, results and a geometry code which can be linked to shapefiles

The UK Data Support provide many boundary maps, including simplified Parliamentary constituencies for England.

Although only a simplified geometry was required it still takes a few seconds to download.

If you wish to extend this analysis to cover Scotland and Wales constituencies, you will need to download Boundary-Lines from the Ordnance Survey but this is at a much more detailed level and not appropriate for swift rendering. For those interested, it may be possible to create your versions using mapshaper

# These are functions to get data. I have previously saved them to local files
#constituencies <- constituencies()
#electionResults <- election_results()

constituencies <- read_csv("hansardData/constituencies.csv")
glimpse(constituencies)
## Observations: 650
## Variables: 9
## $ about                 <chr> "http://data.parliament.uk/resources/146...
## $ constituency_type     <chr> "County", "County", "Borough", "Borough"...
## $ gss_code              <chr> "W07000049", "W07000058", "S14000001", "...
## $ os_name               <chr> NA, "Aberconwy Co Const", "Aberdeen Nort...
## $ ended_date_value      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
## $ ended_date_datatype   <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
## $ label_value           <chr> "Aberavon", "Aberconwy", "Aberdeen North...
## $ started_date_value    <date> 2010-05-06, 2010-05-06, 2005-04-05, 200...
## $ started_date_datatype <chr> "dateTime", "dateTime", "dateTime", "dat...
electionResults <- read_csv("hansardData/electionResults.csv")
glimpse(electionResults)
## Observations: 1,331
## Variables: 9
## $ about                    <chr> "http://data.parliament.uk/resources/...
## $ electorate               <int> 64808, 64031, 62364, 62860, 67165, 73...
## $ majority                 <int> 8361, 3506, 12408, 3282, 3431, 9911, ...
## $ result_of_election       <chr> "Lab Hold", "Lab Hold", "Lab Hold", "...
## $ turnout                  <int> 37701, 43034, 35849, 37960, 45207, 45...
## $ constituency_about       <chr> "http://data.parliament.uk/resources/...
## $ constituency_label_value <chr> "Aberdeen North", "Aberdeen South", "...
## $ election_about           <chr> "http://data.parliament.uk/resources/...
## $ election_label_value     <chr> "2010 General Election", "2010 Genera...
#constituencies$label_value and electionResults$constituency_label_value are equivalent

## Add a couple of columns to show margin of victory as a %

election.2015 <- electionResults %>% 
  filter(election_label_value=="2015 General Election")  %>%  
  left_join(constituencies,by=c("constituency_label_value"="label_value")) %>% 
  mutate(victory_pc=round(100*majority/turnout,1))

# and just show the party winning
election.2015$party <- election.2015$result_of_election %>% 
  map(str_split, pattern = " ") %>% 
  map_chr(c(1,1))

# load the simplified England data using the sf package
fname <- "hansardData/england_parl_2011_gen_clipped.shp" 
boundaries <- st_read(fname)
## Reading layer `england_parl_2011_gen_clipped' from data source `C:\Users\Andrew\Documents\R\mytinyshinysBlog\content\post\hansardData\england_parl_2011_gen_clipped.shp' using driver `ESRI Shapefile'
## Simple feature collection with 533 features and 3 fields
## geometry type:  MULTIPOLYGON
## dimension:      XY
## bbox:           xmin: 82679.8 ymin: 5343.899 xmax: 655604.7 ymax: 657534.1
## epsg (SRID):    NA
## proj4string:    +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +datum=OSGB36 +units=m +no_defs
glimpse(boundaries)  
## Observations: 533
## Variables: 4
## $ altname  <fctr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
## $ name     <fctr> North Somerset, West Bromwich West, Preston, East Ha...
## $ code     <fctr> E14000850, E14001030, E14000885, E14000680, E1400083...
## $ geometry <simple_feature> MULTIPOLYGON (((349333.499 ..., MULTIPOLYG...
# boundaries$code and election.2015$gss_code can be used to join data and shapes

election.2015 provides information on the result and also has a geometry gss_code which has a counterpart in the boundaries, ‘code’. We can now join the two datasets and create maps, using the leaflet package, after a projection transformation.

I have created a couple of maps. The first shows each constituency colour-coded by party and if there was change in party control. For those not familiar with the UK system:-

  • Con: Conservative
  • Green: Green party
  • Lab: Labour
  • LD: Liberal Democrats
  • Spk: Speaker of House (unopposed)
  • UKIP: UK Independence Party

The map takes a few seconds to render

# First transform(using an sf function) for use in leaflet. An informative error occurs otherwise
boundaries.leaflet <- st_transform(boundaries,'+proj=longlat +datum=WGS84')

# Join data
england.leaflet <- 
  boundaries.leaflet %>% 
  left_join(election.2015,by=c("code"="gss_code"))


# change to factors so colorFactor from the leaflet package can be used
england.leaflet$result_of_election <- as.factor(england.leaflet$result_of_election)

# Determine the number of distinct colours to show and determine appropriate colours
levels(england.leaflet$result_of_election) #8
## [1] "Con Gain"   "Con Hold"   "Green Hold" "Lab Gain"   "Lab Hold"  
## [6] "LD Hold"    "Spk Hold"   "UKIP Gain"
# select colors from http://www.stat.columbia.edu/~tzheng/files/Rcolor.pdf

mycols <- c("blue","deepskyblue","darkolivegreen1","firebrick1","lightpink","lightgoldenrod1","black","mediumpurple")



factpal <- colorFactor(mycols, england.leaflet$result_of_election)

labels <- sprintf(
  "<strong>%s</strong><br/>%s Majority %s",
  england.leaflet$name,england.leaflet$result_of_election,england.leaflet$majority
) %>% lapply(htmltools::HTML)


## still takes few secs
england.leaflet %>% 
  leaflet(width =  500) %>% 
  addTiles() %>% 
  addPolygons(weight=1,
              fillOpacity = 1,
              dashArray = "3",
              fillColor=~factpal(result_of_election),
              label=labels,            
              labelOptions=labelOptions(direction="left")) %>%
  addLegend("bottomright", pal = factpal, values = ~result_of_election,
    title = "Results",
    opacity = 1
  )