Published
Edited
Jan 24, 2021
Insert cell
md`# Task A - D3.js Submission 02`
Insert cell
md`Student: **Zhao Yue**, Matric Number: **A0137801L**`
Insert cell
md`This submission discussed about the data source provided by [data.gov.sg](https://data.gov.sg) regarding the data of dwelling units under HDB's management, by flat type and town from 2008 to 2019, the queries are listed below:
- What is the trend of total dwelling units, across different years?
- what are the top 5 district containing the largest numnber of dweling units currently in 2018?
- what is the trend of percentage distribution of different flat types across different years?
- For each flat type, what are the percentage of rental and sold?
`
Insert cell
d3 = require("d3@6")
Insert cell
raw = d3.csv("https://samuelzhaoy.static.observableusercontent.com/files/384a3422a0676bb30031cfe6a302608c10921a723cf16298ffb1e0ed6e51c2c087fb302c3e38a5e7f5589f3d87b52319e69862521a8e66f0b3b4b81f5a7406fc?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%27dwelling-units-under-hdbs-management-by-town-and-flat-type.csv&Expires=1611576000000&Key-Pair-Id=APKAJCHFJLLLU4Y2WVSQ&Signature=luXx2o1rzmqZC-xuph3rXCnQI3O9GNFoQJcHzaK3-vnDzWY1B-pz6D68p544M9FcCOMup6pyG7yaOPm-O2Dh~vQeQvbUHoQt8CfQqxg825aJiMRWPpGnEy2U6Eqz51YsXVgcrfZRp1IW7cRqsgpo5NtKkspoei5aSHX5Bmv0-epE6x~eEd~jvYGJjeEE9pCznZw5dk7-89UCrS5moFBs5~vDrlu7RHLgRnrprgMARtdNx11GBUL5R6qrrmr6LMqvOplcg6JspRZ-e-ydTxH2T27RUbtoPgYFn0EXKwATZoEeGYjmjzBl2GEwlDfzdJhVMNCaBkiN8p9QZ4x7Rg0N5A__")
Insert cell
md`### Query 1: What is the trend of total dwelling units, across different years?`
Insert cell
dwellingUnitPerYears = new Promise(resolve => {

let countPerYear = {};
raw.forEach(record => {
if (!countPerYear.hasOwnProperty(record.financial_year)) {
countPerYear[record.financial_year] = {
year:record.financial_year,
count:parseInt(record.no_of_dwelling_units) }
} else {
countPerYear[record.financial_year].count += parseInt(record.no_of_dwelling_units)
}
});
resolve(Object.values(countPerYear));
})
Insert cell
import { vl } from "@vega/vega-lite-api"
Insert cell
viewof simpleBar = vl.markBar()
.data(dwellingUnitPerYears)
.encode(
vl.x().fieldO("year"),
vl.y().fieldQ("count")
)
.render()
Insert cell
md`### Query 2: what are the top 5 district containing the largest numnber of dweling units in 2018? can we visualze them in sorted order?`
Insert cell
// change the year 2018 to any other year you want to visualize
numberOfDwellingUnitsIn2018 = new Promise(resolve => {
let dataIn2018 = raw.filter(r => r.financial_year == '2018')
let dwellingPerTown = {};
// get data in 2018
dataIn2018.forEach(record => {
if (!dwellingPerTown.hasOwnProperty(record.town_or_estate)) {
dwellingPerTown[record.town_or_estate] = {
town_name: record.town_or_estate,
count: parseInt(record.no_of_dwelling_units)
}
} else {
dwellingPerTown[record.town_or_estate].count += parseInt(record.no_of_dwelling_units)
}
})
// sort the data
resolve(
Object.values(dwellingPerTown).sort((a, b) => b.count - a.count)
);
})
Insert cell
import {table} from "@tmcw/tables/2"
Insert cell
viewof topDiseaseTable = table(numberOfDwellingUnitsIn2018, { title: "number of dwelling units in 2018" })
Insert cell
md`### Query 3: what is the trend of percentage distribution of different flat types across different years?`
Insert cell
data = new Promise(resolve => {
let typeYearCountMap = {};
let categoryNames = [];
raw.forEach(record => {
if (categoryNames.indexOf(record.flat_type) < 0) {
categoryNames.push(record.flat_type)
}
if (!typeYearCountMap.hasOwnProperty(record.financial_year)) {
typeYearCountMap[record.financial_year] = {
name: record.financial_year
}
}
if (!typeYearCountMap[record.financial_year].hasOwnProperty(record.flat_type)) {
typeYearCountMap[record.financial_year][record.flat_type] = parseInt(record.no_of_dwelling_units)
} else {
typeYearCountMap[record.financial_year][record.flat_type] += parseInt(record.no_of_dwelling_units)
}
});
categoryNames.splice(0, 0, 'name');
let output = Object.values(typeYearCountMap)
output.columns = categoryNames
resolve(output);
})
Insert cell
import {swatches} from "@d3/color-legend"
Insert cell
series = d3.stack()
.keys(data.columns.slice(1))
(data)
.map(d => (d.forEach(v => v.key = d.key), d))
Insert cell
color = d3.scaleOrdinal()
.domain(series.map(d => d.key))
.range(series.map(d => d3.interpolateGreens((series.indexOf(d)+1)/series.length)))
.unknown("#ccc")
Insert cell
key = swatches({color, title: "name", columns: "180px"})
Insert cell
chart = {
var height = 600
var margin = ({top: 10, right: 10, bottom: 20, left: 40})
var x = d3.scaleBand()
.domain(data.map(d => d.name))
.range([margin.left, width - margin.right])
.padding(0.1)
var y = d3.scaleLinear()
.domain([0, d3.max(series, d => d3.max(d, d => d[1]))])
.rangeRound([height - margin.bottom, margin.top])
var xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call(g => g.selectAll(".domain").remove())
var yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).ticks(null, "s"))
.call(g => g.selectAll(".domain").remove())
var formatValue = x => isNaN(x) ? "N/A" : x.toLocaleString("en")
var allTypes = [];
series.forEach(element => {
allTypes.push(element.key)
})
console.log(allTypes)
var getTotal = function(item) {
var count = 0;
allTypes.forEach(type => {
if (item.hasOwnProperty(type)) {
count += parseInt(item[type])
}
})
return count
}
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
const panel = svg.append("g")
panel.selectAll("g")
.data(series)
.join("g")
.attr("fill", d => color(d.key))
.selectAll("rect")
.data(d => d)
.join("rect")
.attr("x", (d, i) => x(d.data.name))
.attr("y", d => y(d[1]))
.attr("height", d => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth())
.append("title")
.text(d => `${d.data.name} ${d.key} ${formatValue(d.data[d.key])}`)
series.forEach(flatTypeRecords => {
flatTypeRecords.forEach(d => {
panel.append("text")
.attr("x", x(d.data.name))
.attr("y", y(d[1]) + (y(d[0]) - y(d[1]))/2)
.attr("font-size", "0.6em")
.text(Math.floor((d.data[d.key]/getTotal(d.data)) * 100) + ' %')
})
})
svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);

return svg.node();
}
Insert cell
md`### Query 4: For each flat type, what are the percentage of rental and sold?`
Insert cell
import {select} from "@jashkenas/inputs"
Insert cell
flatTypes = ['1-room', '2-room', '3-room', '4-room', '5-room', 'Executive', 'Studio Apartment', 'HUDC']
Insert cell
selectionValueLabels = flatTypes.map(function(name) {
return {
'value': flatTypes.indexOf(name),
'label': name
}
})
Insert cell
// change the year 2018 to any other year you want to visualize
numberPerTypesOfDwellingUnitsIn2018 = new Promise(resolve => {
let dataIn2018 = raw.filter(r => r.financial_year == '2018')
let dwellingPerTypes = {};
// get data in 2018
dataIn2018.forEach(record => {
if (!dwellingPerTypes.hasOwnProperty(record.flat_type)) {
dwellingPerTypes[record.flat_type] = {}
}
if (!dwellingPerTypes[record.flat_type].hasOwnProperty(record.sold_or_rental)) {
dwellingPerTypes[record.flat_type][record.sold_or_rental] = parseInt(record.no_of_dwelling_units)
} else {
dwellingPerTypes[record.flat_type][record.sold_or_rental] += parseInt(record.no_of_dwelling_units)
}
})
let output = {};
Object.keys(dwellingPerTypes).forEach(key => {
let rentalSoldData = dwellingPerTypes[key];
output[key] = [];
Object.keys(rentalSoldData).forEach(key1 => {
output[key].push({
"category": key1,
"value": rentalSoldData[key1],
"order": Object.keys(rentalSoldData).indexOf(key1)
})
})
})
// sort the data
resolve(
output
);
})
Insert cell
viewof value = select({
title: "Flat Types",
description: "Please select flat types",
options: selectionValueLabels,
})
Insert cell
{
const data = numberPerTypesOfDwellingUnitsIn2018[selectionValueLabels[value].label];
const colors = {
domain: ['Rental Units', 'Sold Units'],
range: ['#416D9D', '#674028']
};

return vl.markArc({outerRadius: 150})
.description('Reproducing http://robslink.com/SAS/democd91/pyramid_pie.htm')
.data(data)
.encode(
vl.theta().fieldQ('value').stack(true).scale({range: [0.75 * Math.PI, 2.75 * Math.PI]}),
vl.color().fieldN('category').title(null).scale(colors).legend({orient: 'right'}),
vl.order().fieldO('order')
)
.config({view: {stroke: null}})
.width(300)
.height(300)
.render();
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more