1. Overview

Singapore held its 13th General Election since independence, amid of COVID-19 pandemic on the 10th July 2020, shortly after the Circuit Breaker was lifted. This election was like no other that Singapore had experienced, there was many special arrangements made to deal with COVID-19 and many gravity of the situation and issues at stake.
The Government has rolled out four COVID-19 packages amounting to some $93 billion as Singapore’s economy is projected to shrink by up to 7 per cent this year, one of the worst recession since the country’s independence in 1965.

This visualization attempts to discover insights behind Singapore’s General Election results for General Elections 2015 and 2020 using tmap package.

2. Challenges and Proposed Sketch

2.1 Major Data and Design challenges and proposed solutions

Challenge A) The availability of the data, there is a need to merge multiple resources. Historical General Election results are available from Data.gov.sg, however number of Electors and GE2020 results are from Elections Department Singapore and 2020 rejected votes from Wikipedia.
Solution A) Combined the data into multiple csv file and cross-check them across different sources before using.

Challenge B) The available Maps are in different format for 2015 (KML) and 2020 (KMZ).
Solution B) Convert them into shape files using either ArcGIS Pro or some of the available online converter e.g MyGeodata Converter. In this case because the 2015 data still requires the additional effort to lookup the data when using ArcGIS Pro, therefore the converter versions are used instead.

Challenge C) There are 10 to 12 opposition parties and some of them did not receive high number of votes, hence it might not be meaningful to display the result individually.
Solution C) Therefore I have grouped the oppositions parties information into one group called opposition.

Challenge D) Due to the limited size in map, it might seems very cramp if includes multiples layers of information in 1 map.
Solution D) Creating more maps to visual them clearer, however only choosing those with insights on the final visualization.

2.2 Proposed Sketch Design

alt text

3. Step-by-step descriptions

3.1 Installation of Packages

packages <- c('sf','tmap','tidyverse','scales')
for(p in packages){
  if(!require(p, character.only = T)){
    install.packages(p)
  }
  library(p, character.only = T)
}

3.2 Loading the 2 different maps for GE2020 and GE2015

There is also the need to mutate the name for the 2015 map to the correct constituency naming as the initial naming e.g kml_1 does not add any value to the map.

mp2020 <- st_read(dsn = "data/electoral_2020", 
                layer = "electoral-boundary-dataset-polygon")
## Reading layer `electoral-boundary-dataset-polygon' from data source `C:\Users\Penny Tuar\Desktop\data\electoral_2020' using driver `ESRI Shapefile'
## Simple feature collection with 31 features and 7 fields
## geometry type:  MULTIPOLYGON
## dimension:      XY
## bbox:           xmin: 103.6057 ymin: 1.158762 xmax: 104.0885 ymax: 1.470783
## geographic CRS: WGS 84
mp2020
## Simple feature collection with 31 features and 7 fields
## geometry type:  MULTIPOLYGON
## dimension:      XY
## bbox:           xmin: 103.6057 ymin: 1.158762 xmax: 104.0885 ymax: 1.470783
## geographic CRS: WGS 84
## First 10 features:
##             Name descriptio    altitudeMo ED_CODE FID       ED_DESC
## 1  TANJONG PAGAR       <NA> clampToGround      TP   2 TANJONG PAGAR
## 2    JALAN BESAR       <NA> clampToGround      JB   3   JALAN BESAR
## 3        PIONEER       <NA> clampToGround      PI   5       PIONEER
## 4          YUHUA       <NA> clampToGround      YH   7         YUHUA
## 5      MARYMOUNT       <NA> clampToGround      MR  12     MARYMOUNT
## 6        HOUGANG       <NA> clampToGround      HG  14       HOUGANG
## 7  MARINE PARADE       <NA> clampToGround      MA  10 MARINE PARADE
## 8     WEST COAST       <NA> clampToGround      WE  11    WEST COAST
## 9  BUKIT PANJANG       <NA> clampToGround      BP  18 BUKIT PANJANG
## 10  YIO CHU KANG       <NA> clampToGround      YK  19  YIO CHU KANG
##          Field_1                       geometry
## 1  TANJONG PAGAR MULTIPOLYGON (((103.8458 1....
## 2    JALAN BESAR MULTIPOLYGON (((103.8738 1....
## 3        PIONEER MULTIPOLYGON (((103.7083 1....
## 4          YUHUA MULTIPOLYGON (((103.7373 1....
## 5      MARYMOUNT MULTIPOLYGON (((103.8523 1....
## 6        HOUGANG MULTIPOLYGON (((103.8855 1....
## 7  MARINE PARADE MULTIPOLYGON (((103.9265 1....
## 8     WEST COAST MULTIPOLYGON (((103.7413 1....
## 9  BUKIT PANJANG MULTIPOLYGON (((103.7758 1....
## 10  YIO CHU KANG MULTIPOLYGON (((103.8408 1....
mp2015 <- st_read(dsn = "data/electoral_2015", 
                layer = "electoral-boundary-2015-kml-polygon") %>%
                mutate(Name=ED_DESC)
## Reading layer `electoral-boundary-2015-kml-polygon' from data source `C:\Users\Penny Tuar\Desktop\data\electoral_2015' using driver `ESRI Shapefile'
## Simple feature collection with 29 features and 6 fields
## geometry type:  POLYGON
## dimension:      XY
## bbox:           xmin: 103.599 ymin: 1.150372 xmax: 104.0975 ymax: 1.477406
## geographic CRS: WGS 84
mp2015
## Simple feature collection with 29 features and 6 fields
## geometry type:  POLYGON
## dimension:      XY
## bbox:           xmin: 103.599 ymin: 1.150372 xmax: 104.0975 ymax: 1.477406
## geographic CRS: WGS 84
## First 10 features:
##                   Name descriptio             ED_DESC ED_CODE          INC_CRC
## 1  HOLLAND-BUKIT TIMAH       <NA> HOLLAND-BUKIT TIMAH      HT 59D1ADB532F37979
## 2         PUNGGOL EAST       <NA>        PUNGGOL EAST      PE 8298D32B826A5881
## 3              PIONEER       <NA>             PIONEER      PI 20CC8109497AD9FE
## 4        TANJONG PAGAR       <NA>       TANJONG PAGAR      TP E2F2C4995BA6A36D
## 5           WEST COAST       <NA>          WEST COAST      WE F4D7255652CDEB2F
## 6                YUHUA       <NA>               YUHUA      YH F53D5507C8899036
## 7           MACPHERSON       <NA>          MACPHERSON      MP 52B177763050C78E
## 8        BUKIT PANJANG       <NA>       BUKIT PANJANG      BP 0666283E37C18BEE
## 9          BUKIT BATOK       <NA>         BUKIT BATOK      BK C9C672B0A67B7DF4
## 10       CHUA CHU KANG       <NA>       CHUA CHU KANG      CK BC7AFC6C8D8A94A6
##        FMEL_UPD_D                       geometry
## 1  20150916151419 POLYGON ((103.8059 1.411095...
## 2  20150916151419 POLYGON ((103.9143 1.385937...
## 3  20150916151419 POLYGON ((103.7083 1.340033...
## 4  20150916151419 POLYGON ((103.842 1.325961,...
## 5  20150916151419 POLYGON ((103.641 1.356886,...
## 6  20150916151419 POLYGON ((103.7342 1.344365...
## 7  20150916151419 POLYGON ((103.8907 1.324443...
## 8  20150916151419 POLYGON ((103.7758 1.364821...
## 9  20150916151419 POLYGON ((103.7576 1.345461...
## 10 20150916151419 POLYGON ((103.7414 1.387056...

3.3 Loading the electoral data for GE2020 and GE2015

Electoral2020 <-read_csv("data/2020_mapdata.csv")
## Parsed with column specification:
## cols(
##   year = col_double(),
##   constituency = col_character(),
##   no_of_registered_electors = col_double(),
##   total_counted_votes = col_double(),
##   total_rejected_votes = col_double(),
##   total_did_not_vote = col_double(),
##   constituency_size = col_double()
## )
glimpse(Electoral2020)
## Rows: 31
## Columns: 7
## $ year                      <dbl> 2020, 2020, 2020, 2020, 2020, 2020, 2020,...
## $ constituency              <chr> "Aljunied", "Ang Mo Kio", "Bishan-Toa Pay...
## $ no_of_registered_electors <dbl> 150821, 185261, 101220, 29948, 35437, 106...
## $ total_counted_votes       <dbl> 143145, 173274, 93679, 28287, 33661, 1015...
## $ total_rejected_votes      <dbl> 1582, 5016, 2049, 533, 586, 1410, 1393, 1...
## $ total_did_not_vote        <dbl> 6094, 6971, 5492, 1128, 1190, 3656, 5732,...
## $ constituency_size         <dbl> 5, 5, 4, 1, 1, 4, 5, 4, 1, 1, 4, 5, 1, 1,...
Electoral2015 <-read_csv("data/2015_mapdata.csv")
## Parsed with column specification:
## cols(
##   year = col_double(),
##   constituency = col_character(),
##   no_of_registered_electors = col_double(),
##   total_counted_votes = col_double(),
##   total_rejected_votes = col_double(),
##   total_did_not_vote = col_double(),
##   constituency_size = col_double()
## )
glimpse(Electoral2015)
## Rows: 29
## Columns: 7
## $ year                      <dbl> 2015, 2015, 2015, 2015, 2015, 2015, 2015,...
## $ constituency              <chr> "Aljunied", "Ang Mo Kio", "Bishan-Toa Pay...
## $ no_of_registered_electors <dbl> 148142, 187771, 129975, 27077, 34317, 119...
## $ total_counted_votes       <dbl> 137474, 172074, 117809, 24972, 32106, 110...
## $ total_rejected_votes      <dbl> 1681, 4927, 2443, 552, 631, 2978, 1048, 2...
## $ total_did_not_vote        <dbl> 8987, 10770, 9723, 1553, 1580, 6628, 7355...
## $ constituency_size         <dbl> 5, 6, 5, 1, 1, 4, 4, 1, 4, 1, 1, 4, 5, 1,...

3.4 Loading the election results for GE2020 and GE2015

In this section, Filter () function is used to select the relevant year data, 2020 and 2015. Afterwhich the results are mutated to show PAP and opposition vote percentage and their differences only.

Result <-read_csv("data/parliamentary-general-election-results-by-candidate-edited.csv")
## Parsed with column specification:
## cols(
##   year = col_double(),
##   constituency = col_character(),
##   constituency_type = col_character(),
##   candidates = col_character(),
##   party = col_character(),
##   vote_count = col_character(),
##   vote_percentage = col_character(),
##   constituency_size = col_double()
## )
Result2020 <- Result %>%
  filter(year=="2020")%>%
  filter(party=="PAP")%>%
  spread(party,vote_percentage)%>%
  mutate("PAP"=as.numeric(PAP))%>%
  mutate("opposition"= 1-PAP) %>%
  mutate("Differences"=PAP-opposition) %>%
  select(constituency,constituency_type,PAP,opposition,Differences)

head(Result2020)
## # A tibble: 6 x 5
##   constituency     constituency_type   PAP opposition Differences
##   <chr>            <chr>             <dbl>      <dbl>       <dbl>
## 1 Aljunied         GRC               0.400      0.599     -0.199 
## 2 Ang Mo Kio       GRC               0.719      0.281      0.438 
## 3 Bishan-Toa Payoh GRC               0.672      0.328      0.345 
## 4 Bukit Batok      SMC               0.548      0.452      0.096 
## 5 Bukit Panjang    SMC               0.537      0.463      0.0746
## 6 Chua Chu Kang    GRC               0.586      0.414      0.173
Result2015 <- Result %>%
  filter(year=="2015")%>%
  filter(party=="PAP")%>%
  spread(party,vote_percentage)%>%
  mutate("PAP"=as.numeric(PAP))%>%
  mutate("opposition"= 1-PAP) %>%
  mutate("Differences"=PAP-opposition) %>%
  select(constituency,constituency_type,PAP,opposition,Differences)

head(Result2015)
## # A tibble: 6 x 5
##   constituency     constituency_type   PAP opposition Differences
##   <chr>            <chr>             <dbl>      <dbl>       <dbl>
## 1 Aljunied         GRC               0.490      0.510     -0.0192
## 2 Ang Mo Kio       GRC               0.786      0.214      0.573 
## 3 Bishan-Toa Payoh GRC               0.736      0.264      0.472 
## 4 Bukit Batok      SMC               0.730      0.270      0.460 
## 5 Bukit Panjang    SMC               0.684      0.316      0.368 
## 6 Chua Chu Kang    GRC               0.769      0.231      0.538

3.5 Combining the election results, electoral data and map.

Left join election results with electoral data first, follow by joining with map.

Combined2020 <- left_join(Electoral2020,Result2020,
                          by = c("constituency"="constituency"))
Combined2020
## # A tibble: 31 x 11
##     year constituency no_of_registere~ total_counted_v~ total_rejected_~
##    <dbl> <chr>                   <dbl>            <dbl>            <dbl>
##  1  2020 Aljunied               150821           143145             1582
##  2  2020 Ang Mo Kio             185261           173274             5016
##  3  2020 Bishan-Toa ~           101220            93679             2049
##  4  2020 Bukit Batok             29948            28287              533
##  5  2020 Bukit Panja~            35437            33661              586
##  6  2020 Chua Chu Ka~           106632           101566             1410
##  7  2020 East Coast             121644           114519             1393
##  8  2020 Holland-Buk~           114973           107318             1999
##  9  2020 Hong Kah No~            28046            26804              403
## 10  2020 Hougang                 26432            25242              272
## # ... with 21 more rows, and 6 more variables: total_did_not_vote <dbl>,
## #   constituency_size <dbl>, constituency_type <chr>, PAP <dbl>,
## #   opposition <dbl>, Differences <dbl>
Combined2015 <- left_join(Electoral2015,Result2015,
                          by = c("constituency"="constituency"))
Combined2015
## # A tibble: 29 x 11
##     year constituency no_of_registere~ total_counted_v~ total_rejected_~
##    <dbl> <chr>                   <dbl>            <dbl>            <dbl>
##  1  2015 Aljunied               148142           137474             1681
##  2  2015 Ang Mo Kio             187771           172074             4927
##  3  2015 Bishan-Toa ~           129975           117809             2443
##  4  2015 Bukit Batok             27077            24972              552
##  5  2015 Bukit Panja~            34317            32106              631
##  6  2015 Chua Chu Ka~           119931           110325             2978
##  7  2015 East Coast              99118            90715             1048
##  8  2015 Fengshan                23427            21593              295
##  9  2015 Holland-Buk~           104491            94280             1543
## 10  2015 Hong Kah No~            28145            26255              554
## # ... with 19 more rows, and 6 more variables: total_did_not_vote <dbl>,
## #   constituency_size <dbl>, constituency_type <chr>, PAP <dbl>,
## #   opposition <dbl>, Differences <dbl>

Before joining to the map, there is a need to change the constituency all to uppercase, as the map data is in uppercase.

Combined2020$constituency <- toupper(Combined2020$constituency)
Combined2015$constituency <- toupper(Combined2015$constituency)
GE2020map <- left_join(mp2020,Combined2020,
                       by = c("Name"="constituency"))                
GE2020map
## Simple feature collection with 31 features and 17 fields
## geometry type:  MULTIPOLYGON
## dimension:      XY
## bbox:           xmin: 103.6057 ymin: 1.158762 xmax: 104.0885 ymax: 1.470783
## geographic CRS: WGS 84
## First 10 features:
##             Name descriptio    altitudeMo ED_CODE FID       ED_DESC
## 1  TANJONG PAGAR       <NA> clampToGround      TP   2 TANJONG PAGAR
## 2    JALAN BESAR       <NA> clampToGround      JB   3   JALAN BESAR
## 3        PIONEER       <NA> clampToGround      PI   5       PIONEER
## 4          YUHUA       <NA> clampToGround      YH   7         YUHUA
## 5      MARYMOUNT       <NA> clampToGround      MR  12     MARYMOUNT
## 6        HOUGANG       <NA> clampToGround      HG  14       HOUGANG
## 7  MARINE PARADE       <NA> clampToGround      MA  10 MARINE PARADE
## 8     WEST COAST       <NA> clampToGround      WE  11    WEST COAST
## 9  BUKIT PANJANG       <NA> clampToGround      BP  18 BUKIT PANJANG
## 10  YIO CHU KANG       <NA> clampToGround      YK  19  YIO CHU KANG
##          Field_1 year no_of_registered_electors total_counted_votes
## 1  TANJONG PAGAR 2020                    134494              124137
## 2    JALAN BESAR 2020                    107720               98892
## 3        PIONEER 2020                     24653               23537
## 4          YUHUA 2020                     21351               20032
## 5      MARYMOUNT 2020                     23431               22116
## 6        HOUGANG 2020                     26432               25242
## 7  MARINE PARADE 2020                    139622              130250
## 8     WEST COAST 2020                    146089              138654
## 9  BUKIT PANJANG 2020                     35437               33661
## 10  YIO CHU KANG 2020                     25962               24294
##    total_rejected_votes total_did_not_vote constituency_size constituency_type
## 1                  1933               8424                 5               GRC
## 2                  2948               5880                 4               GRC
## 3                   350                766                 1               SMC
## 4                   406                913                 1               SMC
## 5                   305               1010                 1               SMC
## 6                   272                918                 1               SMC
## 7                  1789               7583                 5               GRC
## 8                  1646               5789                 5               GRC
## 9                   586               1190                 1               SMC
## 10                  413               1255                 1               SMC
##       PAP opposition Differences                       geometry
## 1  0.6310     0.3690      0.2620 MULTIPOLYGON (((103.8458 1....
## 2  0.6536     0.3464      0.3072 MULTIPOLYGON (((103.8738 1....
## 3  0.6200     0.3800      0.2400 MULTIPOLYGON (((103.7083 1....
## 4  0.7054     0.2946      0.4108 MULTIPOLYGON (((103.7373 1....
## 5  0.5504     0.4496      0.1008 MULTIPOLYGON (((103.8523 1....
## 6  0.3879     0.6121     -0.2242 MULTIPOLYGON (((103.8855 1....
## 7  0.5774     0.4226      0.1548 MULTIPOLYGON (((103.9265 1....
## 8  0.5168     0.4832      0.0336 MULTIPOLYGON (((103.7413 1....
## 9  0.5373     0.4627      0.0746 MULTIPOLYGON (((103.7758 1....
## 10 0.6082     0.3918      0.2164 MULTIPOLYGON (((103.8408 1....
GE2015map <- left_join(mp2015,Combined2015,
                       by = c("Name"="constituency"))
GE2015map
## Simple feature collection with 29 features and 16 fields
## geometry type:  POLYGON
## dimension:      XY
## bbox:           xmin: 103.599 ymin: 1.150372 xmax: 104.0975 ymax: 1.477406
## geographic CRS: WGS 84
## First 10 features:
##                   Name descriptio             ED_DESC ED_CODE          INC_CRC
## 1  HOLLAND-BUKIT TIMAH       <NA> HOLLAND-BUKIT TIMAH      HT 59D1ADB532F37979
## 2         PUNGGOL EAST       <NA>        PUNGGOL EAST      PE 8298D32B826A5881
## 3              PIONEER       <NA>             PIONEER      PI 20CC8109497AD9FE
## 4        TANJONG PAGAR       <NA>       TANJONG PAGAR      TP E2F2C4995BA6A36D
## 5           WEST COAST       <NA>          WEST COAST      WE F4D7255652CDEB2F
## 6                YUHUA       <NA>               YUHUA      YH F53D5507C8899036
## 7           MACPHERSON       <NA>          MACPHERSON      MP 52B177763050C78E
## 8        BUKIT PANJANG       <NA>       BUKIT PANJANG      BP 0666283E37C18BEE
## 9          BUKIT BATOK       <NA>         BUKIT BATOK      BK C9C672B0A67B7DF4
## 10       CHUA CHU KANG       <NA>       CHUA CHU KANG      CK BC7AFC6C8D8A94A6
##        FMEL_UPD_D year no_of_registered_electors total_counted_votes
## 1  20150916151419 2015                    104491               94280
## 2  20150916151419 2015                     34466               32795
## 3  20150916151419 2015                     25458               23598
## 4  20150916151419 2015                    130752              116633
## 5  20150916151419 2015                     99300               90640
## 6  20150916151419 2015                     22617               20836
## 7  20150916151419 2015                     28511               26299
## 8  20150916151419 2015                     34317               32106
## 9  20150916151419 2015                     27077               24972
## 10 20150916151419 2015                    119931              110325
##    total_rejected_votes total_did_not_vote constituency_size constituency_type
## 1                  1543               8668                 4               GRC
## 2                   381               1290                 1               SMC
## 3                   664               1196                 1               SMC
## 4                  2466              11653                 5               GRC
## 5                  2432               6228                 4               GRC
## 6                   392               1389                 1               SMC
## 7                   437               1775                 1               SMC
## 8                   631               1580                 1               SMC
## 9                   552               1553                 1               SMC
## 10                 2978               6628                 4               GRC
##       PAP opposition Differences                       geometry
## 1  0.6660     0.3340      0.3320 POLYGON ((103.8059 1.411095...
## 2  0.5177     0.4823      0.0354 POLYGON ((103.9143 1.385937...
## 3  0.7635     0.2365      0.5270 POLYGON ((103.7083 1.340033...
## 4  0.7771     0.2229      0.5542 POLYGON ((103.842 1.325961,...
## 5  0.7857     0.2143      0.5714 POLYGON ((103.641 1.356886,...
## 6  0.7355     0.2645      0.4710 POLYGON ((103.7342 1.344365...
## 7  0.6560     0.3440      0.3120 POLYGON ((103.8907 1.324443...
## 8  0.6838     0.3162      0.3676 POLYGON ((103.7758 1.364821...
## 9  0.7302     0.2698      0.4604 POLYGON ((103.7576 1.345461...
## 10 0.7691     0.2309      0.5382 POLYGON ((103.7414 1.387056...

3.6 Plotting the maps - Electoral Information for GE2020 and GE2015

g2020 = tm_shape(GE2020map) +
  tm_fill("no_of_registered_electors",
          style = "quantile",
          palette = "Blues",
          title = "Registered Electors") +
  tm_borders(alpha = 0.5) +
  tm_text("constituency_type",size=0.5, remove.overlap = T) +
  tm_layout(main.title = "Electoral information for Singapore General Election 2020",
            main.title.position = "center",
            main.title.size = 1.2,
            legend.outside = FALSE,
            legend.height = 0.5, 
            legend.width = 0.5,
            legend.outside.position = "bottom",
            legend.text.size = 0.5,
            frame = FALSE)
g2020

g2015 = tm_shape(GE2015map) +
  tm_fill("no_of_registered_electors",
          style = "quantile",
          palette = "BuGn",
          title = "Registered Electors") +
  tm_borders(alpha = 0.5) +
  tm_text("constituency_type",size=0.5) +
  tm_layout(main.title = "Electoral information for Singapore General Election 2015",
            main.title.position = "center",
            main.title.size = 1.2,
            legend.outside = FALSE,
            legend.height = 0.5, 
            legend.width = 0.5,
            legend.outside.position = "bottom",
            legend.text.size = 0.5,
            frame = FALSE)

g2015

3.7 Plotting the maps - Number of people who did not vote for GE2020 and GE2015

No_vote2020 = tm_shape(GE2020map) +
  tm_fill("total_did_not_vote",
          style = "quantile",
          palette = "Blues",
          id="total_did_not_vote",
          title = "Number of no vote") +
  tm_borders(alpha = 0.5) +
  tm_text("Name",size=0.5) +
  tm_layout(main.title = "Number of people who did not vote for Singapore General Election 2020",
            main.title.position = "center",
            main.title.size = 0.8,
            legend.outside = FALSE,
            legend.height = 0.5, 
            legend.width = 0.5,
            legend.outside.position = "bottom",
            legend.text.size = 0.5,
            frame = FALSE)
No_vote2020

No_vote2015 = tm_shape(GE2015map) +
  tm_fill("total_did_not_vote",
          style = "quantile",
          palette = "BuGn",
          id="total_did_not_vote",
          title = "Number of no vote") +
  tm_borders(alpha = 0.5) +
  tm_text("Name",size=0.5) +
  tm_layout(main.title = "Number of people who did not vote for Singapore General Election 2015",
            main.title.position = "center",
            main.title.size = 0.8,
            legend.outside = FALSE,
            legend.height = 0.5, 
            legend.width = 0.5,
            legend.outside.position = "bottom",
            legend.text.size = 0.5,
            frame = FALSE)

No_vote2015

3.8 Plotting the maps - Differences in vote percentage between PAP and Opposition for GE2020 and GE2015

g2020R = tm_shape(GE2020map) +
  tm_fill("Differences",
          style = "quantile",
          palette = "-Blues",
          title = "Differences in vote % ") +
  tm_borders(alpha = 0.5) +
  tm_text("Name",size=0.5, remove.overlap = T) +
  tm_layout(main.title = "How much incremental % did the Ruling Party win in General Election 2020 by Constituency",
            main.title.position = "center",
            main.title.size = 0.8,
            legend.outside = FALSE,
            legend.height = 0.5, 
            legend.width = 0.5,
            legend.outside.position = "bottom",
            legend.text.size = 0.5,
            frame = FALSE)
g2020R

g2015R = tm_shape(GE2015map) +
  tm_fill("Differences",
          style = "quantile",
          palette = "-BuGn",
          title = "Differences in vote %") +
  tm_borders(alpha = 0.5) +
  tm_text("Name",size=0.5) +
  tm_layout(main.title = "How much incremental % did the Ruling Party win in Singapore General Election 2015 by Constituency",
            main.title.position = "center",
            main.title.size = 0.8,
            legend.outside = FALSE,
            legend.height = 0.5, 
            legend.width = 0.5,
            legend.outside.position = "bottom",
            legend.text.size = 0.5,
            frame = FALSE)

g2015R

Filtering the top quantile (20%) for clearer comparison for GE2020 and GE2015

Tq2020 <- GE2020map %>%
  filter(GE2020map$Differences <0.096)

Tq2015 <- GE2015map %>%
  filter(GE2015map$Differences <0.266)
Concern2020 = tm_shape(GE2020map) +
  tm_fill("white") +
  tm_borders("grey", lwd = 0.5) +
  tm_shape(Tq2020) +
  tm_fill("skyblue") +
  tm_borders(alpha = 0.5)+
  tm_text("Name",size=0.5) +
  tm_layout(main.title = "Constituencies whereby the gap in vote percentage is within the lowest first quantile for 2020",
            main.title.position = "center",
            main.title.size = 0.8,
            legend.outside = FALSE,
            legend.height = 0.5, 
            legend.width = 0.5,
            legend.outside.position = "bottom",
            legend.text.size = 0.5,
            frame = FALSE)

Concern2020

Concern2015 = tm_shape(GE2015map) +
  tm_fill("white") +
  tm_borders("grey", lwd = 0.5) +
  tm_shape(Tq2015) +
  tm_fill("turquoise3") +
  tm_borders(alpha = 0.5)+
  tm_text("Name",size=0.5) +
  tm_layout(main.title = "Constituencies whereby the gap in vote percentage is within the lowest first quantile for 2015",
            main.title.position = "center",
            main.title.size = 0.8,
            legend.outside = FALSE,
            legend.height = 0.5, 
            legend.width = 0.5,
            legend.outside.position = "bottom",
            legend.text.size = 0.5,
            frame = FALSE)

Concern2015

tmap_mode("view")
## tmap mode set to interactive viewing

4. Final Visualisation and Useful Information revealed

4.1 Final Visualisation

tmap_arrange(No_vote2015,No_vote2020,Concern2015,Concern2020)

4.2 Useful information revealed

Insight 1It is interesting to know that the top 3 constituencies that have the highest number of people who did not vote remains the same following, Tanjong Pagar, Marine Parade and Ang Mo Kio for both GE2020 and GE2015. Ang Mo Kio being one of the highest number of registered electors, it is not surprising, however there could be some additional investigation to be done for Tanjong Pagar and Marine Parade, it could be that historically Tanjong Pagar is a walkover constituency so the residents are not used to voting, or it could be that these are the overseas voters who did not vote due to the limited polling stations overseas.
It is also worth noting that in GE2015, most of the higher number of registered electors constituencies were situation in the north-east area, such as Ang Mo Kio, Pasir Ris-Punggol, Aljunied and Tampines however for GE2020 West Coast was also one of the high number of registered electors constituency, partly due to the changes in electoral division whereby it absorbed portions from Chua Chu Kang GRC and Hong Kah North SMC.

Insight 2From the differences in vote percentage between PAP and opposition, there seems to be a worrying trend, the highest winning gap between PAP (Ruling Party) and opposition has reduced while the highest winning gap for opposition has increased. In GE2015, most of the concerns area to PAP should be in the east such as Aljunied, East Coast etc, however in GE2020, those area of concerns still remains as a concern and more constituencies such as Bukit Panjang and West Coast have been added to as poor performing constituencies for PAP.
In GE2015, West Coast was one of the best performing yet in GE2020, it has become one of the worst, interesting the number of people who did not vote in West Coast is higher than the differences in PAP wins, if everyone has voted, the result might have changed. This also shows that PAP cannot be complacent with any good performing constituency for the next election too.

Sources

Data.gov.sg:(https://data.gov.sg/dataset/parliamentary-general-election-results?resource_id=4706f2cb-a909-4cc0-bd3d-f366c34cf6af)
Elections Department Singapore: (https://www.eld.gov.sg/finalresults2020.html)
Wikipedia:(https://en.wikipedia.org/wiki/2020_Singaporean_general_election)
The Straits Times:(https://www.straitstimes.com/politics/ge2020-singaporeans-will-go-to-the-polls-on-july-10)
The Straits Times:(https://www.straitstimes.com/politics/over-4700-votes-cast-by-citizens-in-polling-stations-abroad)