Hands-On Exercise 08-2

Author

Chun-Han

Published

March 2, 2026

Modified

March 2, 2026

Overview

Proportional symbol maps (also known as graduate symbol maps) are a class of maps that use the visual variable of size to represent differences in the magnitude of a discrete, abruptly changing phenomenon, e.g. counts of people. Like choropleth maps, you can create classed or unclassed versions of these maps. The classed ones are known as range-graded or graduated symbols, and the unclassed are called proportional symbols, where the area of the symbols are proportional to the values of the attribute being mapped. In this hands-on exercise, you will learn how to create a proportional symbol map showing the number of wins by Singapore Pools’ outlets using an R package called tmap.

1 Getting Started

1.1 Installing and loading the packages

In this hands-on exercise, the key R package use is tmap package in R. Beside tmap package, four other R packages will be used. They are:

  • readr for importing delimited text file,
  • tidyr for tidying data,
  • dplyr for wrangling data and
  • sf for handling geospatial data.

Among the four packages, readr, tidyr and dplyr are part of tidyverse package.

The code chunk below will be used to install and load these packages in RStudio.

pacman::p_load(sf, tmap, tidyverse)

1.2 Importing Data into R

The data set use for this hands-on exercise is called SGPools_svy21. The data is in csv file format.

The code chunk below uses read_csv() function of readr package to import SGPools_svy21.csv into R as a tibble data frame called sgpools.

sgpools <- read_csv("data/SGPools_svy21.csv")

After importing the data file into R, it is important for us to examine if the data file has been imported correctly.

The code chunk below shows list() is used to do the job.

list(sgpools) 
[[1]]
# A tibble: 306 × 7
   NAME           ADDRESS POSTCODE XCOORD YCOORD `OUTLET TYPE` `Gp1Gp2 Winnings`
   <chr>          <chr>      <dbl>  <dbl>  <dbl> <chr>                     <dbl>
 1 Livewire (Mar… 2 Bayf…    18972 30842. 29599. Branch                        5
 2 Livewire (Res… 26 Sen…    98138 26704. 26526. Branch                       11
 3 SportsBuzz (K… Lotus …   738078 20118. 44888. Branch                        0
 4 SportsBuzz (P… 1 Sele…   188306 29777. 31382. Branch                       44
 5 Prime Serango… Blk 54…   552542 32239. 39519. Branch                        0
 6 Singapore Poo… 1A Woo…   731001 21012. 46987. Branch                        3
 7 Singapore Poo… Blk 64…   370064 33990. 34356. Branch                       17
 8 Singapore Poo… Blk 88…   370088 33847. 33976. Branch                       16
 9 Singapore Poo… Blk 30…   540308 33910. 41275. Branch                       21
10 Singapore Poo… Blk 20…   560202 29246. 38943. Branch                       25
# ℹ 296 more rows

1.3 Creating a sf data frame from an aspatial data frame

The code chunk below converts sgpools data frame into a simple feature data frame by using st_as_sf() of sf packages

sgpools_sf <- st_as_sf(sgpools, 
                       coords = c("XCOORD", "YCOORD"),
                       crs= 3414)

Things to learn from the arguments above:

The coords argument requires you to provide the column name of the x-coordinates first then followed by the column name of the y-coordinates. The crs argument required you to provide the coordinates system in epsg format. EPSG: 3414 is Singapore SVY21 Projected Coordinate System. You can search for other country’s epsg code by refering to epsg.io.

glimpse(sgpools_sf)
Rows: 306
Columns: 6
$ NAME              <chr> "Livewire (Marina Bay Sands)", "Livewire (Resorts Wo…
$ ADDRESS           <chr> "2 Bayfront Avenue, #01-01 The Shoppes at Marina Bay…
$ POSTCODE          <dbl> 18972, 98138, 738078, 188306, 552542, 731001, 370064…
$ `OUTLET TYPE`     <chr> "Branch", "Branch", "Branch", "Branch", "Branch", "B…
$ `Gp1Gp2 Winnings` <dbl> 5, 11, 0, 44, 0, 3, 17, 16, 21, 25, 22, 25, 31, 27, …
$ geometry          <POINT [m]> POINT (30841.56 29598.56), POINT (26703.87 265…

2 Drawing Proportional Symbol Map

To create an interactive proportional symbol map in R, the view mode of tmap will be used.

The code churn below will turn on the interactive mode of tmap.

tmap_mode("view")

2.1 Started with an interactive point symbol map

The code chunks below are used to create an interactive point symbol map.

tm_shape(sgpools_sf) + 
  tm_bubbles(fill = "red",
           size = 1,
           col = "black",
           lwd = 1)

2.2 Make it proportional

To draw a proportional symbol map, we need to assign a numerical variable to the size visual attribute. The code chunks below show that the variable Gp1Gp2Winnings is assigned to size visual attribute.

tm_shape(sgpools_sf) + 
  tm_bubbles(fill = "red",
             size = "Gp1Gp2 Winnings",
             col = "black",
             lwd = 1)

2.3 Give it a different colour

The proportional symbol map can be further improved by using the colour visual attribute. In the code chunks below, OUTLET_TYPE variable is used as the colour attribute variable.

tm_shape(sgpools_sf) + 
  tm_bubbles(fill = "OUTLET TYPE", 
             size = "Gp1Gp2 Winnings",
             col = "black",
             lwd = 1)

2.4 Multiple maps

An impressive and little-know feature of tmap’s view mode is that it also works with faceted plots. The argument sync in tm_facets() can be used in this case to produce multiple maps with synchronised zoom and pan settings.

tm_shape(sgpools_sf) + 
  tm_bubbles(fill = "OUTLET TYPE", 
             size = "Gp1Gp2 Winnings",
             col = "black",
             lwd = 1) + 
  tm_facets(by= "OUTLET TYPE",
            nrow = 1,
            sync = TRUE)

Before you end the session, it is wiser to switch tmap’s Viewer back to plot mode by using the code chunk below.

tmap_mode("plot")