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

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more