Published
Edited
Jan 31, 2020
Insert cell
md`# Typology Assessment`
Insert cell
viewof input = html`<input type=file accept=".csv">`
Insert cell
csv = {
const data = d3.csvParse(await Files.text(input), d3.autoType);
return data;
}
Insert cell
accessor = "EUI"
Insert cell
chart = {
const svg = d3.create("svg")
.attr("width", width).attr("height", height);
svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);
const g = svg.append("g")
.selectAll("g")
.data(Array.from(data.keys()))
.join("g")
.attr("transform", d => `translate(0, ${y(d)})`)
.attr("fill", d => color(d.match(/T\d+/)[0]));

//range
g.append("path")
.attr("stroke", "#e8e8e8")
.attr("d", d => `
M${x(data.get(d).range[0])},${y.bandwidth() / 2}
H${x(data.get(d).range[1])}`
);
//range ends
g.append("g")
.selectAll("circle")
.data(d => data.get(d).range)
.join("circle")
.attr("r", 3)
.attr("cx", d => x(d))
.attr("cy", y.bandwidth() / 2);
//quartiles
g.append("rect")
.attr("y", 0)
.attr("x", d => x(data.get(d).quartiles[0]))
.attr("height", y.bandwidth())
.attr("width", d => x(data.get(d).quartiles[2]) - x(data.get(d).quartiles[0]));
g.append("path")
.attr("stroke", "#ffffff")
.attr("stroke-width", 2)
.attr("shape-rendering", "crispEdges")
.attr("d", d => `
M${x(data.get(d).quartiles[1])},0
V${y.bandwidth()}`
);


return svg.node();
}
Insert cell
data = {
let grouped = _.groupBy(csv, d => `${d.PROGRAM} (${d.TYPE})`);
const map = new Map();
for (let [k, v] of Object.entries(grouped)) {
map.set(k, bins(v, accessor));
}
return map;

}
Insert cell
function bins(data, accessor) {
data.sort((a, b) => a[accessor] - b[accessor]);
const values = data.map(d => d[accessor]);
const min = values[0];
const max = values[values.length - 1];
const q1 = d3.quantile(values, 0.25);
const q2 = d3.quantile(values, 0.50);
const q3 = d3.quantile(values, 0.75);
const iqr = q3 - q1; // interquartile range
const r0 = Math.max(min, q1 - iqr * 1.5);
const r1 = Math.min(max, q3 + iqr * 1.5);
data.quartiles = [q1, q2, q3];
data.range = [r0, r1];
data.outliers = data.filter(v => v.y < r0 || v.y > r1);
return data;
}
Insert cell
// keys = Array.from(data.keys()).sort((a, b) => +a.replace("T", "") - +b.replace("T", ""));
keys = _.sortBy(Array.from(data.keys()), d => d.replace(/\s\(T\d*\)/, ""))
Insert cell
y = d3.scaleBand()
.domain(keys)
.range([margin.top, height - margin.bottom])
.padding(0.4)
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).tickSizeOuter(0))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").remove())
Insert cell
x = d3.scaleLinear()
.domain([d3.min(Array.from(data.keys()), key => data.get(key).range[0]), d3.max(Array.from(data.keys()), key => data.get(key).range[1])])
.range([margin.left, width - margin.right])
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${margin.top})`)
.call(d3.axisTop(x).ticks(5))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.05)
.attr("y2", height - margin.top - margin.bottom));
Insert cell
color = d => {
let lookup = {
"T0": "#a8a7ab",
"T1": "#8958a4",
"T2": "#04a8dc",
"T3": "#0c7c3e",
"T4": "#559459",
"T5": "#f4847b",
"T6": "#aa85bd",
"T7": "#f59087",
"T8": "#c0bec0",
"T9": "#f69c93",
"T10": "#d3d2d3",
"T11": "#f6a9a1",
"T12": "#84b280",
"T13": "#e8e8e8",
"T14": "#f7b6af",
"T15": "#d4bcdb",
"T16": "#b8ddae",
"T17": "#f8da63",
"T18": "#8dd1e9",
"T19": "#f8c3be",
"T20": "#f8d0cd",
"T21": "#b65e07",
"T22": "#fee8a3",
"T23": "#d0f1f4",
"T24": "#f99f1b",
"T25": "#faaa3b",
"T26": "#fcb65b",
"T27": "#fdc377",
"T28": "#fecf92",
"T29": "#fedcae",
"T30": "#ffe9cc"
}
return lookup[d];
}
Insert cell
Insert cell
Insert cell
height = 800
Insert cell
width = 900
Insert cell
margin = ({top: 20, right: 20, bottom: 30, left: 150})
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