Skip to main content

Evolution of the Singapore Population

Executive Summary

  • A quick and cursory look at Singapore population evolution based on publicly available dataset
  • Singapore has aged dramatically from 2000 to 2016
  • A comparision with population in 2000 with that of 2016 shows that a shortage working age population had to be made up with permanent residents or naturalised citizens
  • The proportion of working age Singapore residents has been decreasing since 2011 and looks likely to continue so in the next few years due to the lack of younger population projected to enter the work force
  • The ethnic mix of Singapore has remained relatively constant from 2000 -- 2016


Singapore has always been population-challenged. The fact that Singapore will always depend on the quality its people in order to survive in this globalised world has been known since its founding in 1965.

Its size represents a glass ceiling limiting economic potential and in turn societal stability. Without a high quality labor force, Singapore would find it hard to attract investors, talents and business interest when compared to neighbours such as Indonesia which has a large market to which companies can sell to.

As Singapore develops and acquires some of the characteristics of developed economies such as an aging labour force and the need to move towards a consumer/knowledge-driven economy (if that is even possible in Sinpapore's context), demographics will play a crucial role in the future of Singapore. The effects of demographic evolution are magnified in Singapore since unlike other countries, we have neither natural resources to cushion the effects of global economic vagaries nor a hinterland for us to be self-sustaining in terms of key need such as water and food.

Thus to satisfy my curiousity about how the Singapore demographics have changed over the years, I decided to make a quick and cursory analysis of publicly available data from

# Data Description

  • Publicly available data set retrieved from
  • URL here
  • In this analysis, we only take a look at population data from the year 2000 to 2016

Load libraries

In [63]:
import re
import requests
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import itertools

%matplotlib inline
plt.rcParams["figure.figsize"] = (12,6)

Get data

In [64]:
url = ""

api_req = requests.get(url).json()

api_response = dict(api_req)

data_records = api_response["result"]["records"]

df = pd.DataFrame(data_records).drop("_id", axis=1).replace("na",np.nan).dropna()

df.columns = ["Type","Age Category","Population","Year"]

df["Population"] = df["Population"].astype(
df["Year"] = df["Year"].astype(

df = df.loc[(df["Age Category"].str.contains(".*Years$|85 Years \& Over")) & (df["Year"]>=2000),:]

df["Age Category"] = df.loc[:,"Age Category"].map(lambda x: re.sub("  "," ",x.strip()))

Evolution of total residents in Singapore

In [65]:
total_residents = df.loc[(2000<=df["Year"]) & (df["Year"]<=2016) & (df["Type"] == "Total Residents"), 
                         ["Age Category", "Population", "Year"]]

f, ax = plt.subplots()

with sns.color_palette("RdBu_r", 7):
    age_bin_edges = np.arange(0,86,5)
    age_categories = []
    for i, j in enumerate(age_bin_edges[:-1]):
        age_categories.append(str(j)+" - "+str(age_bin_edges[i+1]-1)+" Years")
    age_categories.append("85 Years & Over")
    for year in np.arange(2000, 2017, 2):
        tmp_df = (total_residents.loc[total_residents["Year"]==year].set_index("Age Category")
        if year != 2000 and year != 2016:
            linewidth = 0.75
            alpha = 0.75
            alpha = 1
        ax.plot(np.arange(len(age_categories)), tmp_df["Population"], linewidth = linewidth, alpha = alpha)
    for i in ax.get_xticklabels():
    ax.legend(np.arange(2000,2017,2), bbox_to_anchor=(1.2,1))
    ax.set_xlabel("Age Category", fontsize = 20)
    ax.set_ylabel("Popuplation", fontsize = 20)

The blue (red) line represents the population distribution in the year 2000 (2016). Two things are notable in this graph.

First, the population between 55 to 74 years old has more than doubled during the intervening years.

Second, there is a dip in the population aged 15 to 24 years old in 2000. Suppose, the population remains unchanged for 15 years (i.e. shifting the blue line 3 steps down), that very dip in population would be in the 30 to 39 year age categories in the year 2015. In other words, Singapore would face a significant crunch in the most productive age segment of her population, if it were not for immigrants.

The effects of demographic evolution plays out slowly but surely. Imagine the knock-on effects on the standard of living of the entire Singapore had the lack of manpower not been corrected. The lack of labour will drive wages and hence living costs up. Not to mention, companies (both local and international) will find Singapore a comparatively less attractive place to do business. I know this sounds alarmist but Singapore is a country that has very little room for policy errors. Once businesses uproot and move some where else, it is very hard for them to come back. Once our reputation as a financial/business/logistics hub is damaged, it is very hard to restore.

Age group population as a proportion of Sinagpore population (2000 -- 2016)

In [66]:
def calculateProportion(df_group):
    return pd.DataFrame({"Year": df_group["Year"],
                         "Age Category": df_group["Age Category"], 
                         "Proportion": df_group["Population"]/np.sum(df_group["Population"])})

total_residents_proportion = total_residents.groupby(["Year"]).apply(lambda x: calculateProportion(x))
In [67]:
def mapAgeClass(x):
    if x in age_categories[:4]:
        return "less than 20 Years"
    elif x in age_categories[4:-5]:
        return "20 - 64 Years"
        return "65 Years & Over"
total_residents_proportion["Age Class"] = (total_residents_proportion["Age Category"]
                                           .map(lambda x: mapAgeClass(x))
In [68]:
total_residents_proportion_grp = total_residents_proportion.groupby(["Age Class","Year"]).agg({"Proportion":np.sum})
In [69]:
g = sns.FacetGrid(data = total_residents_proportion_grp.reset_index(),
                  row = "Age Class",
                  aspect = 2,
                  size = 4,
                  sharey = False,
                  row_order = ["less than 20 Years","20 - 64 Years","65 Years & Over"]

_ =, "Year", "Proportion")

This is another look at the data.

As can be seen, the proportion of the populace under 20 years old, has been on a steady decrease. From >28% in 2000 to <22% in 2016. The opposite is true for the populace over 65 years old.

At the same time, the working age population peaked in 2011 and began a downtrend till 2016. The peak in working age population in 2011 corresponds to a sharp increase in the elderly population. This means that the decrease in working age population is mostly driven by large corhorts of people entering the retiree status.

This would create knock-on effects such as:

  • Increased load on healthcare services
  • Smaller economically productive population to sustain economic growth
  • Rising wages & costs of living which in turn might lead to a lack of global competitiveness if it is not at least matched by an increase in individual productivity

Of course, the effects of demographics changes are complicated, intertwined and can hardly be summarised by a few statistics. Most importantly a lot of it will depend on how Singaporeans respond to the changes. For example, a surge in entrepreneurship in young Singaporeans might fuel a surge in Singapore-grown enterprises and hence employment and productivity.

Evolution of ethnicity mix

In [70]:
ethnic_df = (df.loc[(df["Type"].isin(["Total Malays", "Total Chinese", 
                                     "Total Indians", "Other Ethnic Groups (Total)"])) &

ethnic_df_sum = ethnic_df.groupby("Year").sum().reset_index()

ethnic_df = ethnic_df.merge(ethnic_df_sum, on = "Year", how = "outer", suffixes = ("_group", "_total"))

ethnic_df["Proportion"] = ethnic_df["Population_group"]/ethnic_df["Population_total"]

def mapEthnicity(x):
    if x=="Total Chinese":
        return "Chinese"
    elif x=="Total Malays":
        return "Malays"
    elif x=="Total Indians":
        return "Indians"
        return "Others"
ethnic_df["Type"] = ethnic_df["Type"].map(mapEthnicity)
In [71]:
f, ax = plt.subplots()

ethnic_groups = ["Chinese", "Malays", "Indians", "Others"]

for group_count, group in enumerate(ethnic_groups):
    if group_count==0:
        bottom = None
        bottom = np.nansum((ethnic_df.loc[ethnic_df["Type"].isin(ethnic_groups[:group_count])]

    tmp_df = ethnic_df.loc[ethnic_df["Type"]==group,:]["Year"])), tmp_df["Proportion"], bottom=bottom, width = 1, edgecolor = "black")
_=ax.legend(ethnic_groups, bbox_to_anchor = (1.2,1))
_=ax.set_xticklabels(tmp_df["Year"], rotation=45)
_=ax.set_ylabel("Proportion of population",fontsize=20)

In terms of ethnic mix, the Singapore government has maintained roughly status quo since 2000. I believe this to be wise as a rapid change in ethnic mixture often lead to disintegration of societal unity and pockets of people un-integrated with the broader society as seen in several cities in UK and France.


This is just a spur-of-the-moment look at how Singapore's demographics have changed over the last 16 years. It is by no means complete. I make no claims of clairvoyance and the possible paths a society can take in response to demographics changes cannot be charted out for certain.

However, it is clear that Singapore's population is aging and will continue to age in a rather rapid fashion. In the next 10 years, there will be huge cohorts of people entering not just retirement but also ripe old age where the provision of care is crucial and expensive. The working age population will also continue to decline, exacerbating the burden of each productive citizen to support the Singapore society as a whole and reducing workforce availability and to some extent competitiveness.