1.0 Overview

The purpose of this visualization document is to incorporate visual interactivity to study the changing patterns of the demographic structure of Singapore based on the data given by the Singapore department of statistics website(from www.Singstat.gov.sg). This dataset has the demographic characteristics related to planning area, subzone, type of dwelling, age group, sex and population for the years 2011-2019. For this visualization task, we will be taking the data related to all years from 2011- 2019 initially, as as we go forward we will consider data every four years i.e. we will consider data for years-2011, 2015 and 2019.

For this visualization, we have given more attention to the changing prime demographic characteristics such as planning area and Age group. Given this focus, we have made three visualizations. The first one is a slope chart describing change of Population per age group over time. For the second one, three population pyramids are shown for the years 2011, 2015 and 2019each of which shows the variation of population in different age groups for each gender. And lastly,population heatmaps are made for the planning area and age group characteristics for three years - 2011, 2015 and 2019. All these visualizations have interactivity embedded, which has been made using plotly. The embedded interactivity enables us to extract knowledge from the visual design.

2.0 Major data and design challenges

3.0 Plan to overcome the data and design challenges

3.1 The Plan

  1. The data manipulation challenge as indicated in point 1 in the above section can be addressed by the use of data manipulation packages in R such as dplyr. Thus, There is a scope to change the column names using a function called rename().

  2. The disaggregated data problem for certain charts as indicated in point 2 in the above section can be solved by getting aggregated data pertaining to our use case through the aggregate() function.

  3. The ordinal data ordering problem while plotting as indicated in point 3 in the above section can be solved by changing the column to a factor and manually indicating the ordered levels as part of the levels parameter.

  4. For customization problem as indicated in point 4 in the above section, We can see that the ggplot package supports good degree of customizations such as setting the themes, changing the inclination of the labels in the axis through the theme function, changing the colour scale etc.

  5. As a solution to point 5 in the above section, for enabling interactive visual design, we can see that the plotly package provides functions to support interactivity in ggplot.

  6. For the label and dimensions customization problem as indicated in point 6 in the above section, we can use the layout function, which is a standard function in R, where we can specify the labels and the dimensions where we want to place them in the visualization. This layout function can be referred from ggplotly() through a %>% symbol.

3.2 The Sketch

4.0 Step by step description of the Data Visualization

4.1 Installing and loading the required packages

As a prior to the visualizations, we need to install/load some important R packages that will aid the use of certain visualization functions. The packages tidyverse, dplyr, tidyr, ggthemes and plotly which are required for this visualization are installed/loaded by using the following function.

packages = c('tidyverse','dplyr','tidyr','ggthemes','plotly')

for(p in packages){
  if(!require(p, character.only = T)){
    install.packages(p)
  }
  library(p, character.only = T)
}

4.2 Loading the data

The initial data provided for the visualization task was ‘respopagesextod2011to2019.csv’ and it had demographic data for all the years from 2011 to 2019. Thus we will load this data into ‘demo’ using the read_csv function.

demo = read_csv("Singapore Residents by Planning AreaSubzone Age Group Sex and Type of Dwelling June 20112019/respopagesextod2011to2019.csv")

4.3 Data Manipulation Part 1

To rename the abbreviated column names such as ‘PA’,‘AG’ etc in order to avoid ambiguity, the rename function is used to rename these columns as per their metadata.

demo <- demo %>%
  rename('Planning_Area'='PA',
         'Subzone'='SZ',
         'Age_Group'='AG',
         'Population'='Pop',
         'Year'='Time',
         'Dwelling_Type'= 'TOD')

The structure of the demographic data is changed to a dataframe to ensure that a dataframe structure is used throught the visualization.

demo <- as.data.frame(demo)
head(demo)
##   Planning_Area                Subzone Age_Group   Sex
## 1    Ang Mo Kio Ang Mo Kio Town Centre    0_to_4 Males
## 2    Ang Mo Kio Ang Mo Kio Town Centre    0_to_4 Males
## 3    Ang Mo Kio Ang Mo Kio Town Centre    0_to_4 Males
## 4    Ang Mo Kio Ang Mo Kio Town Centre    0_to_4 Males
## 5    Ang Mo Kio Ang Mo Kio Town Centre    0_to_4 Males
## 6    Ang Mo Kio Ang Mo Kio Town Centre    0_to_4 Males
##                             Dwelling_Type Population Year
## 1                 HDB 1- and 2-Room Flats          0 2011
## 2                        HDB 3-Room Flats         10 2011
## 3                        HDB 4-Room Flats         30 2011
## 4          HDB 5-Room and Executive Flats         50 2011
## 5 HUDC Flats (excluding those privatised)          0 2011
## 6                       Landed Properties          0 2011

Next, we look upon the statistics of each individual field in the dataframe and also the class type of the field.

summary(demo)
##  Planning_Area        Subzone           Age_Group             Sex           
##  Length:883728      Length:883728      Length:883728      Length:883728     
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##  Dwelling_Type        Population           Year     
##  Length:883728      Min.   :   0.00   Min.   :2011  
##  Class :character   1st Qu.:   0.00   1st Qu.:2013  
##  Mode  :character   Median :   0.00   Median :2015  
##                     Mean   :  39.83   Mean   :2015  
##                     3rd Qu.:  10.00   3rd Qu.:2017  
##                     Max.   :2860.00   Max.   :2019

Next, we know that the column Age_group is of ordinal data and that we should arrange the classes inside this column so that order is maintained during the visualization. We do this by changing the type of the Age group column to factor and indicate the levels in the order which we intend to use it for visualization.

demo$Age_Group <- factor(demo$Age_Group, levels=c("0_to_4","5_to_9","10_to_14","15_to_19","20_to_24","25_to_29","30_to_34","35_to_39","40_to_44","45_to_49","50_to_54","55_to_59","60_to_64","65_to_69", "70_to_74", "75_to_79","80_to_84","85_to_89","90_and_over"))

In order to view the changing demographic patterns over 4 year intervals in the age sex pyramid and the heatmap, we can have separate dataframes for the years 2011, 2015, 2019. For this purpose, we will use the filter function to filter the corresponding years and store the resulting data in separate dataframes.

demo2019 = demo %>%
  filter(demo$Year == 2019)
demo2011 = demo %>%
  filter(demo$Year == 2011)
demo2015 = demo %>%
  filter(demo$Year == 2015)

Next, we look upon the statistics of each individual field in the dataframes corresponding to individual years and also the class type of the field.

summary(demo2011)
##  Planning_Area        Subzone             Age_Group         Sex           
##  Length:98192       Length:98192       0_to_4  : 5168   Length:98192      
##  Class :character   Class :character   5_to_9  : 5168   Class :character  
##  Mode  :character   Mode  :character   10_to_14: 5168   Mode  :character  
##                                        15_to_19: 5168                     
##                                        20_to_24: 5168                     
##                                        25_to_29: 5168                     
##                                        (Other) :67184                     
##  Dwelling_Type        Population           Year     
##  Length:98192       Min.   :   0.00   Min.   :2011  
##  Class :character   1st Qu.:   0.00   1st Qu.:2011  
##  Mode  :character   Median :   0.00   Median :2011  
##                     Mean   :  38.64   Mean   :2011  
##                     3rd Qu.:  10.00   3rd Qu.:2011  
##                     Max.   :2860.00   Max.   :2011  
## 
summary(demo2015)
##  Planning_Area        Subzone             Age_Group         Sex           
##  Length:98192       Length:98192       0_to_4  : 5168   Length:98192      
##  Class :character   Class :character   5_to_9  : 5168   Class :character  
##  Mode  :character   Mode  :character   10_to_14: 5168   Mode  :character  
##                                        15_to_19: 5168                     
##                                        20_to_24: 5168                     
##                                        25_to_29: 5168                     
##                                        (Other) :67184                     
##  Dwelling_Type        Population           Year     
##  Length:98192       Min.   :   0.00   Min.   :2015  
##  Class :character   1st Qu.:   0.00   1st Qu.:2015  
##  Mode  :character   Median :   0.00   Median :2015  
##                     Mean   :  39.81   Mean   :2015  
##                     3rd Qu.:  10.00   3rd Qu.:2015  
##                     Max.   :2480.00   Max.   :2015  
## 
summary(demo2019)
##  Planning_Area        Subzone             Age_Group         Sex           
##  Length:98192       Length:98192       0_to_4  : 5168   Length:98192      
##  Class :character   Class :character   5_to_9  : 5168   Class :character  
##  Mode  :character   Mode  :character   10_to_14: 5168   Mode  :character  
##                                        15_to_19: 5168                     
##                                        20_to_24: 5168                     
##                                        25_to_29: 5168                     
##                                        (Other) :67184                     
##  Dwelling_Type        Population           Year     
##  Length:98192       Min.   :   0.00   Min.   :2019  
##  Class :character   1st Qu.:   0.00   1st Qu.:2019  
##  Mode  :character   Median :   0.00   Median :2019  
##                     Mean   :  41.08   Mean   :2019  
##                     3rd Qu.:  20.00   3rd Qu.:2019  
##                     Max.   :2440.00   Max.   :2019  
## 

4.4 Creating the slope chart

Now, the slope chart is created. Here we intend to show the changing population numbers over the years from 2011- 2019 for each age group. Before creating this graph, we need to have data in the form where the population is aggregated with respect to the age group and year. Thus we use the aggregate function and store the result in a new dataframe called ‘demo_new’. To create this we use the Year as the x axis variable and the Population as the y axis variable.Since we want to view it per age group, we have the parameter group = Age group. geom_line() is used to create the line chart for each age group. Here they are used with aesthetics where age group is given to be colored. The size of the line is kept as 0.5. geom_point() is used to create the points of the population for each age group in every year. Here they are used with aesthetics where age group is given to be colored. The size of the line is kept as 1. theme_minimal() is used to remove the gray background (non data component), which is the default setting for a R based visualization. The labs() function is used to provide the x and y axis labels for the visualization.

Interactivity: Interactivity in this plot is enabled using the ggplotly() function. This function converts the ggplot( ) to a plotly object suitable for interactivity. The ggplot created is given as the ggplot object for the ggplotly() function. Tooltip is provided using the tooltip function. The text for the tooltip is defined as part of the aesthetics in the original ggplot object. The width and height parameters are adjusted to provide more clarity of the visual object presented. The title and the caption are given through the layout function(). The layout dimensions such as plot margins and the caption placement are adjusted as needed. This layout function is referred by the ggplotly through the pipe symbol.

demo_new<- aggregate(Population~Age_Group+Year,data=demo,FUN=sum)
head(demo_new)
##   Age_Group Year Population
## 1    0_to_4 2011     188620
## 2    5_to_9 2011     208420
## 3  10_to_14 2011     240810
## 4  15_to_19 2011     261030
## 5  20_to_24 2011     255460
## 6  25_to_29 2011     262990
sc = ggplot(data = demo_new, aes(x = factor(Year), y = Population, group = Age_Group, text = paste("Age group :",Age_Group,"\nYear :",Year, "\nPopulation :",Population))) +
  geom_line(aes(color = Age_Group), size = 0.5) +
  geom_point(aes(color = Age_Group), size = 1)  +
  theme_minimal()+
  labs(x = "Year", y=" Population")
ggplotly(p = sc, session = "knitr", tooltip = "text", width = 850, height = 720) %>% layout(title = 'Slope chart describing change of Population per age group over time',
         margin = list(l = 50, r = 50, t = 110, b = 110),
         annotations = list(text = 'Source: Singstat.gov.sg',
                            font = list(size = 12),
                            showarrow = FALSE,
                            xref = 'paper', x = -0.03,
                            yref = 'paper', y = -0.2))

4.5 Data Manipulation Part 2

For the purpose of using the demographic data for creating the heatmap, we need the data in an aggregated form. Thus we use the function called aggregate to create an aggregated dataframe. Since we will need to create the population pyramid for multiple years, aggregated dataframe is created for the years 2011, 2015, 2019(for studying population characteristics over 4 year intervals). The population is aggregated per planning area and age group.

demo2019_agg = aggregate(Population ~ Planning_Area+Age_Group,data = demo2019, FUN = sum)
demo2019_agg = as.data.frame(demo2019_agg)
head(demo2019_agg)
##   Planning_Area Age_Group Population
## 1    Ang Mo Kio    0_to_4       5420
## 2         Bedok    0_to_4      10020
## 3        Bishan    0_to_4       2850
## 4      Boon Lay    0_to_4          0
## 5   Bukit Batok    0_to_4       7130
## 6   Bukit Merah    0_to_4       6100

For this aggregated dataset too, We repeat the conversion of the age group to factor with ordering the labels in the levels parameter to facilitate properly ordered ordinal age group levels in the visualization.

demo2019_agg$Age_Group <- factor(demo2019_agg$Age_Group, levels=c("0_to_4","5_to_9","10_to_14","15_to_19","20_to_24","25_to_29","30_to_34","35_to_39","40_to_44","45_to_49","50_to_54","55_to_59","60_to_64","65_to_69", "70_to_74", "75_to_79","80_to_84","85_to_89","90_and_over"))
demo2011_agg = aggregate(Population ~ Planning_Area+Age_Group,data = demo2011, FUN = sum)
demo2011_agg = as.data.frame(demo2011_agg)
head(demo2011_agg)
##   Planning_Area Age_Group Population
## 1    Ang Mo Kio    0_to_4       7670
## 2         Bedok    0_to_4      13130
## 3        Bishan    0_to_4       3740
## 4      Boon Lay    0_to_4          0
## 5   Bukit Batok    0_to_4       6760
## 6   Bukit Merah    0_to_4       7830
demo2011_agg$Age_Group <- factor(demo2011_agg$Age_Group, levels=c("0_to_4","5_to_9","10_to_14","15_to_19","20_to_24","25_to_29","30_to_34","35_to_39","40_to_44","45_to_49","50_to_54","55_to_59","60_to_64","65_to_69", "70_to_74", "75_to_79","80_to_84","85_to_89","90_and_over"))
demo2015_agg = aggregate(Population ~ Planning_Area+Age_Group,data = demo2015, FUN = sum)
demo2015_agg = as.data.frame(demo2015_agg)
head(demo2015_agg)
##   Planning_Area Age_Group Population
## 1    Ang Mo Kio    0_to_4       6800
## 2         Bedok    0_to_4      11720
## 3        Bishan    0_to_4       3440
## 4      Boon Lay    0_to_4          0
## 5   Bukit Batok    0_to_4       5500
## 6   Bukit Merah    0_to_4       7240
demo2015_agg$Age_Group <- factor(demo2015_agg$Age_Group, levels=c("0_to_4","5_to_9","10_to_14","15_to_19","20_to_24","25_to_29","30_to_34","35_to_39","40_to_44","45_to_49","50_to_54","55_to_59","60_to_64","65_to_69", "70_to_74", "75_to_79","80_to_84","85_to_89","90_and_over"))

4.6 Creation of the Age Sex population pyramid

Now, the age sex pyramid is created for the three years - 2011, 2015 and 2019. To create this we initially use the Age group as the x axis variable and the Population as the y axis variable to create a stacked bar representation and flip it later to create the pyramid.Sex is used as the ‘fill’ parameter in the aes function. The y variable (Population) in the aes function is used with an ifelse statement to facilitate dual axis representation of bars according to the Sex with one side reversed. geom_bar() is used to create the bar chart first. theme_minimal() is used to remove the gray background (non data component), which is the default setting for a R based visualization. scale_y_continuous() is used with the limits parameter set as ‘max(demo$Population) * c(-80,80))’ to establish the population axis according to the values in the column. The multiplier ‘c(-80,80)’ is set through a series of trial and error to ensure that the bars are shown fully in both sides. coord_flip() is used to swap the x and y axis to create a pyramid. The labs() function is used to provide a title, caption and the x and y axis labels for the visualization. options(scipen = 999) is used to avoid the x axis population numbers being represented in scientific notation.

Interactivity: Interactivity in this plot is enabled using the ggplotly() function. This function converts the ggplot( ) to a plotly object suitable for interactivity. The ggplot created is given as the ggplot object for the ggplotly() function. Tooltip is provided using the tooltip function. The text for the tooltip is defined as part of the aesthetics in the original ggplot object. The width and height parameters are adjusted to provide more clarity of the visual object presented. The title and the caption are given through the layout function(). The layout dimensions such as plot margins and the caption placement are adjusted as needed. This layout function is referred by the ggplotly through the pipe symbol.

options(scipen=999)
ag1 = ggplot(demo2011, aes(x = Age_Group, fill = Sex,
                 y = ifelse(test = Sex == "Males",
                            yes = -Population, no = Population), text = paste("Age group :",Age_Group,"\nSex :",Sex))) + 
  geom_bar(stat = "identity") +
  scale_y_continuous(labels = abs, limits = max(demo$Population) * c(-80,80)) +theme_minimal()+
  coord_flip() + labs(x = "Age Group", y = "Population")
ggplotly(p = ag1, session = "knitr", tooltip = "text", width = 850, height = 720) %>% layout(title = 'Age-Sex Population pyramid for 2011',
         margin = list(l = 50, r = 50, t = 110, b = 110),
         annotations = list(text = 'Source: Singstat.gov.sg',
                            font = list(size = 12),
                            showarrow = FALSE,
                            xref = 'paper', x = -0.03,
                            yref = 'paper', y = -0.2))
options(scipen=999)
ag2 = ggplot(demo2015, aes(x = Age_Group, fill = Sex,
                 y = ifelse(test = Sex == "Males",
                            yes = -Population, no = Population), text = paste("Age group :",Age_Group,"\nSex :",Sex))) + 
  geom_bar(stat = "identity") +
  scale_y_continuous(labels = abs, limits = max(demo$Population) * c(-80,80)) +theme_minimal()+
  coord_flip() + labs(x = "Age Group", y = "Population")
ggplotly(p = ag2, session = "knitr", tooltip = "text", width = 850, height = 720) %>% layout(title = 'Age-Sex Population pyramid for 2015',
         margin = list(l = 50, r = 50, t = 110, b = 110),
         annotations = list(text = 'Source: Singstat.gov.sg',
                            font = list(size = 12),
                            showarrow = FALSE,
                            xref = 'paper', x = -0.03,
                            yref = 'paper', y = -0.2))
options(scipen=999)
ag3 = ggplot(demo2019, aes(x = Age_Group, fill = Sex,
                 y = ifelse(test = Sex == "Males",
                            yes = -Population, no = Population), text = paste("Age group :",Age_Group,"\nSex :",Sex))) + 
  geom_bar(stat = "identity") +
  scale_y_continuous(labels = abs, limits = max(demo$Population) * c(-80,80)) +theme_minimal()+
  coord_flip() + labs(x = "Age Group", y = "Population")
ggplotly(p = ag3, session = "knitr", tooltip = "text", width = 850, height = 720) %>% layout(title = 'Age-Sex Population pyramid for 2019',
         margin = list(l = 50, r = 50, t = 110, b = 110),
         annotations = list(text = 'Source: Singstat.gov.sg',
                            font = list(size = 12),
                            showarrow = FALSE,
                            xref = 'paper', x = -0.03,
                            yref = 'paper', y = -0.2))

4.7 Creation of the Population heatmap

Now, the population heatmap is created for the three years - 2011, 2015 and 2019. To create this we use the aggregated data (demo_agg), Age group as the x axis variable and the Planning area as the y axis variable.Population is used as the ‘fill’ parameter in the aes function. geom_tile() is used to create a tile, which is none other than a heatmap. The colour is given to be white to ensure that the edges of the tiles are white, thus promoting visibility of the gradient color. scale_fill_gradient2() is used to fill the tiles with a color gradient according to the population. The tile pertaining to the planning area - age group combination with lower population is given the ‘blue’ color and those with the highest population is given the ‘red’ color. The color for the mid is given as ‘white’ and the ‘midpoint’ is set as ‘10000’. theme_minimal() is used to remove the gray background (non data component), which is the default setting for a R based visualization. The theme() function is used with the axis.text.x parameter to tilt the label by a degree of 45 in order to prevent the overlapping of labels. The labs() function is used to provide a title, caption and the x and y axis labels for the visualization.

Interactivity: Interactivity in this plot is enabled using the ggplotly() function. This function converts the ggplot( ) to a plotly object suitable for interactivity. The ggplot created is given as the ggplot object for the ggplotly() function. Tooltip is provided using the tooltip function. The text for the tooltip is defined as part of the aesthetics in the original ggplot object. The width and height parameters are adjusted to provide more clarity of the visual object presented. The title and the caption are given through the layout function(). The layout dimensions such as plot margins and the caption placement are adjusted as needed. This layout function is referred by the ggplotly through the pipe symbol.

t1 = ggplot(data = demo2011_agg, aes(x=Age_Group, y=Planning_Area, fill=Population, text = paste("Age group :",Age_Group,"\nPlanning Area :",Planning_Area, "\nPopulation :",Population))) + 
  geom_tile()+
  #scale_fill_gradient2(low="blue", high="red", midpoint = 10000)+theme_minimal()+
  theme(axis.text.x = element_text(angle = 45, vjust = 1, 
    size = 10, hjust = 1))+
  labs(x = "Age group", y="Planning Area")
ggplotly(p = t1, session = "knitr", tooltip = "text", width = 850, height = 720) %>% layout(title = 'Heatmap of population for Planning area and Age group 
       characteristics in 2011',
         margin = list(l = 50, r = 50, t = 60, b = 60),
         annotations = list(text = 'Source: Singstat.gov.sg',
                            font = list(size = 12),
                            showarrow = FALSE,
                            xref = 'paper', x = -0.03,
                            yref = 'paper', y = -0.2))
t2 = ggplot(data = demo2015_agg, aes(x=Age_Group, y=Planning_Area, fill=Population, text = paste("Age group :",Age_Group,"\nPlanning Area :",Planning_Area, "\nPopulation :",Population))) + 
  geom_tile()+
  #scale_fill_gradient2(low="blue", high="red", midpoint = 10000)+theme_minimal()+
  theme(axis.text.x = element_text(angle = 45, vjust = 1, 
    size = 10, hjust = 1))+
  labs(x = "Age group", y="Planning Area")
ggplotly(p = t2, session = "knitr", tooltip = "text", width = 850, height = 720) %>% layout(title = 'Heatmap of population for Planning area and Age group 
       characteristics in 2015',
         margin = list(l = 50, r = 50, t = 60, b = 60),
         annotations = list(text = 'Source: Singstat.gov.sg',
                            font = list(size = 12),
                            showarrow = FALSE,
                            xref = 'paper', x = -0.03,
                            yref = 'paper', y = -0.2))
t3 = ggplot(data = demo2019_agg, aes(x=Age_Group, y=Planning_Area, fill=Population, text = paste("Age group :",Age_Group,"\nPlanning Area :",Planning_Area, "\nPopulation :",Population))) + 
  geom_tile()+
  #scale_fill_gradient2(low="blue", high="red", midpoint = 10000)+theme_minimal()+
  theme(axis.text.x = element_text(angle = 45, vjust = 1, 
    size = 10, hjust = 1))+
  labs(x = "Age group", y="Planning Area")
ggplotly(p = t3, session = "knitr", tooltip = "text", width = 850, height = 720) %>% layout(title = 'Heatmap of population for Planning area and Age group 
       characteristics in 2019',
         margin = list(l = 50, r = 50, t = 60, b = 60),
         annotations = list(text = 'Source: Singstat.gov.sg',
                            font = list(size = 12),
                            showarrow = FALSE,
                            xref = 'paper', x = -0.03,
                            yref = 'paper', y = -0.2))

5.0 Final data visualization with description and insights

Slope chart describing change of Population per age group over time

Description:

The purpose of this visualization is to show the change of Population per age group over time. We can hover over the slopes to find information about a particular point in the slopes. The tooltip provides the information about the particular point in the slope relating to the age group, year and population. Each age group is represented by a different color in the graph.

Insights:

  1. It is seen that the slope with the maximum steepness belongs to Age group 65-69. We can infer from this that the people who belong to age group 65-69 have the highest rate of population increase over the years from 2011-2019.
  2. It is seen from the graph that the people who belong to age groups 0 to 4 and 5 to 9 have almost constant/no change in population over the years from 2011-2019.

Age Sex population pyramid

Description:

The purpose of this visualization is to compare the population proportion of males and females in different age groups over the years with 4 year intervals (2011, 2015 and 2019). The same length of the bars corresponding to males and females indicates that there is a balanced proportion, whereas unequal length of bars indicate a skewed proportion towards a particular gender. We can hover over the graph to find information about a particular bar. The tooltip provides the information about the particular bar relating to age group and sex.

Insights:

  1. Over the three years, we see from the graph that for the older age groups (60 + age), there is skewness in the female population bars. This indicates that the population consists of more females than males in the older age groups over all the years.

  2. From the graph, we see that the pyramid shape does not seem to change much, which indicates that the proportion of males to females holds almost constant as years pass by.

Population heatmap for Planning area-Age group

Description:

The purpose of this visualization is to show the heatmap for the age group and the planning area combination for the years 2011, 2015 and 2019 along with interactive elements. This will enable us to know the areas where the population density is high/low and will also help us see which age group people are present more/less in which planning area. The darker the blue color is, the lesser the population. And similarly the lighter the blue color is, the higher the population. We can hover over the tilemap to find information about a particular point. The tooltip provides the information about the particular point in the tile relating to the age group, planning area and population.

Insights:

  1. From the heatmap, we see that over the years, the infant population and the middle aged population has been increasing in Punggol area.
  2. We see that Jurong west area has maintained almost same population over the years. Changi and Changi Bay also have maintained the same population with ver few number of people living in these areas over all these years.