Published
Edited
Jan 23, 2021
Insert cell
md`# Task A - D3.js Submission 01`
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 weekly infections disease recorded from Jan.2012 to Sep.2020. The purpse of the visualisation is to examine:

- The top 20 categories of disease with highest total infection cases from Jan.2012 to Sep.2020.
- The overall trend of the top categories of disease accross different years.
- The trend of each of the disease among the top category diseases.
- The the occurances of each disease in different seasons.
`
Insert cell
import {table} from "@tmcw/tables/2"
Insert cell
d3 = require("d3@6")
Insert cell
raw = d3.csv("https://samuelzhaoy.static.observableusercontent.com/files/c363d0a38fd775ba0914f1d8f91e55e4acfa7b3539411d5038495f11dd31bad9987963ebd84f7d372986bcf93fba7f6fcf6e33aace3ca664eecaa9d8d6dd462b?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%27weekly-infectious-disease-bulletin-cases.csv&Expires=1611446400000&Key-Pair-Id=APKAJCHFJLLLU4Y2WVSQ&Signature=CIfmvXN9Ggu9IRGEi8OSajcWt-NwQywSaHWwQkE-02YADz5~vbI5Nvt1RjwQwai-lasoxr-Xw0aWgK9C1R-rxNKfSW8uasJlSUaED34FjQNBCpzSP2RrylpWZ~b00DUNnd9JnwE89UJHy9UdXtxQSwZSdVPaYj9BCEdDGMjLqY5A9oFl7vxURlc9~w8t-FUzEU1d-KrcfQZdJJrugSTYWB80jqeKIWGvh4iM-vHjU5m-k76sp6uiJgofr9FcL0UqcCSMHEBHGT-qmvP2B8LAOPn5ECiLvP6AyqMixg2lh4InAnnB9XmbgSSxELfdx7PVH-eKPYThUgdQYlPN-5DVZg__")
Insert cell
md`### Query 1: identify the top 20 disease accross 2012 - 2020`
Insert cell
topDisease = new Promise(resolve => {

var diseaseCount = {};
// accumulating count
raw.forEach(function (item, index) {
if (!diseaseCount.hasOwnProperty(item.disease)) {
diseaseCount[item.disease] = parseInt(item["no._of_cases"]);
} else {
diseaseCount[item.disease] += parseInt(item["no._of_cases"]);
}
});
// transform into array
var diseaseCountArray = [];
Object.keys(diseaseCount).forEach(function (key, index) {
diseaseCountArray.push(
{
"name":key,
"count":diseaseCount[key]
}
);
});
// sort in descending order
diseaseCountArray = diseaseCountArray.sort((a, b) => {
return b.count - a.count;
}).slice(0,20)
// return the top 20 diseases
resolve(diseaseCountArray);
})
Insert cell
md`now we have the table contents of the top 20 diseases across the recent years, lets present them in a table`
Insert cell
viewof topDiseaseTable = table(topDisease, { title: "top diseases from 2012 - 2020" })
Insert cell
md`### Query 2: identify the trend of total occurence of Top 20 diseases across 2012 - 2020`
Insert cell
md`### Query 3: identify the trend of each of the disease among the top category diseases.`
Insert cell
md`Now we need further filter out the raw data, combine weekly data into month and year.`
Insert cell
filteredRecords = new Promise(resolve => {
const topDiseaseNames = topDisease.map( d => d.name );
var filteringData = raw.filter( r => topDiseaseNames.indexOf(r.disease) >= 0 )
// accumulating count
filteringData.forEach(function (item, index) {
item.year = item.epi_week.split('-')[0];
item.week = parseInt(item.epi_week.split('-W')[1]);
item.month = Math.ceil(item.week / 4.35);
});
//
filteringData.forEach(function (item, index) {
if (item.disease == 'HFMD') {
item.disease = 'Hand, Foot Mouth Disease';
}
});
resolve(filteringData);
});
Insert cell
occurenceByYear = new Promise(resolve => {

var yearlyRecord = {};
const topDiseaseNames = topDisease.map( d => d.name );
// accumulating count
filteredRecords.forEach(function (item, index) {
if (!yearlyRecord.hasOwnProperty(item.year)) {
yearlyRecord[item.year] = {name:item.year};
topDiseaseNames.forEach(function (name, index) {
yearlyRecord[item.year][name] = 0;
})
yearlyRecord[item.year][item.disease] = parseInt(item["no._of_cases"]);
} else {
if (yearlyRecord[item.year].hasOwnProperty(item.disease) == false) {
yearlyRecord[item.year][item.disease] = parseInt(item["no._of_cases"]);
} else {
yearlyRecord[item.year][item.disease] += parseInt(item["no._of_cases"]);
}
}
});
// here we found that actually HFMD is Hand Foot Mouth Disease, hence we should combine the two.
Object.values(yearlyRecord).forEach(function (record, index) {
record["Hand, Foot Mouth Disease"] += record["HFMD"];
})
resolve(yearlyRecord);
})
Insert cell
md`now let's plot them out`
Insert cell
import {swatches} from "@d3/color-legend"
Insert cell
data = new Promise ( resolve => {
var topDiseaseNames = topDisease.map( d => d.name );
topDiseaseNames = topDiseaseNames.filter(e => e !== 'HFMD')
var renderData = Object.values(occurenceByYear);
renderData.columns = topDiseaseNames;
renderData.columns.splice( 0, 0, "name" )
resolve(renderData);
})
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.interpolateSpectral((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")
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

svg.append("g")
.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])}`);

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

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

return svg.node();
}
Insert cell
md`### Query 4: identify the correlation between seasons and each of the disease`
Insert cell
import {select} from "@jashkenas/inputs"
Insert cell
import { vl } from "@vega/vega-lite-api"
Insert cell
topDiseaseNames = topDisease.map( d => d.name ).filter(e => e !== 'HFMD');

Insert cell
selectionValueLabels = topDiseaseNames.map(function(name) {
return {
'value': topDiseaseNames.indexOf(name),
'label': name
}
})
Insert cell
getSeasonalData = function(index) {
let disease = topDiseaseNames[index]
let formData = {};
filteredRecords.forEach(record => {
var monthYear = record.year+'_'+record.month
if (record.month <= 3) {
monthYear = record.year+'_spring'
} else if (record.month <= 6) {
monthYear = record.year+'_summer'
} else if (record.month <= 9) {
monthYear = record.year+'_autum'
} else if (record.month <= 13) {
monthYear = record.year+'_winter'
}
if (record.disease == disease) {
if (formData.hasOwnProperty(monthYear) == false) {
formData[monthYear] = {
timeline: monthYear,
count: parseInt(record['no._of_cases'])
};
} else {
formData[monthYear].count += parseInt(record['no._of_cases']);
}
}
})
return Object.values(formData);
}
Insert cell
getSeasonalData(0)
Insert cell
viewof value = select({
title: "Disease Name",
description: "Please select the disease",
options: selectionValueLabels,
})
Insert cell
viewof simpleBar = vl.markBar()
.data(getSeasonalData(value))
.encode(
vl.x().fieldO("timeline"),
vl.y().fieldQ("count")
)
.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