Published
Edited
Mar 12, 2021
Fork of Google EEMs
Importers
Insert cell
md`# MIT EEMs`
Insert cell
xlsx = require('https://bundle.run/xlsx@0.14.1')
Insert cell
viewof binary = html`<input type=file>`
Insert cell
input = new Uint8Array(await Files.buffer(binary))
Insert cell
wb = xlsx.read(input, {type: 'array'})
Insert cell
excel = wb.Sheets["Baseline"];
Insert cell
data = xlsx.utils.sheet_to_json(excel);
Insert cell
height = 500
Insert cell
margin = ({top: 20, right: 20, bottom: 30, left: 50})
Insert cell
Insert cell
v_margin = ({top: 80, right: 100, bottom: 50, left: 340})
Insert cell
Insert cell
Insert cell
d3 = require("d3@^6.1")
Insert cell
_ = require('lodash@4.17.15/lodash.js').catch(() => window["_"])
Insert cell
chroma = require('chroma-js')
Insert cell
d3.range(1, 13).map(r => ["E" + r, "C" + r, "S" + r ]).flat()
Insert cell
building = binary.name.replace(".xlsx", "")
Insert cell
ecm_data = {
// const filtered = data.filter(d => d.Run != "Metered" && d.Run != "Baseline");
// const grouped = _.groupBy(filtered, d=> d.Run);
const map = new Map();
let baselineGHG = xlsx.utils.sheet_to_json(wb.Sheets["Baseline"]);
baselineGHG = baselineGHG.map(d => calcghg(d));
for (const key of Object.keys(b_lookup)) {
let excel = wb.Sheets[key];
let dat = xlsx.utils.sheet_to_json(excel);
let f = dat.map((d, i) => Math.max((baselineGHG[i+1] - calcghg(d))/2204, 0));
map.set(key, quartiles(f));
}

return map;
}
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
calcghg = d => {
let e = d3.range(1, 13).map(r => "E" + r).map(u => d[u] * areas[building] * factors.E || 0);
let c = d3.range(1, 13).map(r => "C" + r).map(u => d[u] * areas[building] * factors.C || 0);
let s = d3.range(1, 13).map(r => "S" + r).map(u => d[u] * areas[building] * factors.S || 0);
return _.sum(e) + _.sum(c) + _.sum(s);
}
Insert cell
//lbs per kbtu
factors = ({E: 0.1885, S: 0.1509, C: 0.0801})
Insert cell
//areas in sf
areas = ({
Building_2: 133168,
Building_10: 144463,
Building_13: 183169,
Building_16: 116006,
Building_18: 116900,
Building_37: 102777,
Building_46: 418300,
Building_56: 143940,
Building_66: 133170,
Building_68: 260390,
Building_76: 367689,
Building_E14: 173875,
Building_36: 144887,
Building_48: 57255,
Building_E25: 156676
})
Insert cell
quartiles = (data) => {

const values = data.sort((a, b) => a - b);
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);
values.quartiles = [q1, q2, q3];
values.range = [r0, r1];
values.outliers = values.filter(v => v < r0 || v > r1); // TODO
return values;
}
Insert cell
b_lookup = ({
"Envelope": "Envelope Upgrades",
"HRV": "Heat Recovery Ventilation",
"STR60": "Supply Air Temperature Reset",
"WAC": "Enhanced SAT Reset",
"VENT2": "Ventilation Measures",
"VENT3": "Enhanced Ventilation Measures",
"FanPower": "Fan Energy Reductions",
"LPD": "LPD Reductions",
"Combined_Select_0305": "Select EEMs*",
"Combined": "All Measures Combined"
})
Insert cell
Array.from(ecm_data.values()).map(d => d.range[1])
Insert cell
b_y = d3.scaleBand()
.domain(Object.keys(b_lookup).reverse())
.range([b_height - b_margin.bottom, b_margin.top])
.padding(.1)
.paddingOuter(.5)
Insert cell
b_x = d3.scaleLinear()
.domain([0, d3.max(Array.from(ecm_data.values()).map(d => d.range[1]))]).nice()
.range([b_margin.left, width - b_margin.right])
Insert cell
b_xAxis = g => g
.attr("transform", `translate(0,${v_margin.top})`)
.call(d3.axisTop(b_x).tickPadding(10).tickFormat(d3.format("~s")))
// .call(g => g.select(".domain").remove())
.call(g => g.select(".tick:first-of-type text").clone()
.attr("y", -50)
.attr("x", -3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.html("Annual GHG Reductions (MT CO2e/yr)"))
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("y2", b_height - b_margin.bottom - 80))
Insert cell
b_yAxis = g => g
.attr("transform", `translate(${5},0)`)
.call(d3.axisRight(b_y).tickFormat(d => b_lookup[d]))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").remove())
Insert cell
b_margin = ({top: 80, right: 100, bottom: 50, left: 280})
Insert cell
b_height = 775
Insert cell
showtick = d => {
if (ecm_data.get(d).quartiles[1] < 10) {
return 0;
}
else if (ecm_data.get(d).quartiles[2] - ecm_data.get(d).quartiles[0] < b_x.domain()*.05) {
return 0;
}
else { return 1;}
}
Insert cell
//reset to filter ecms
ecm_highlight = d => {
if (ecm_data.get(d).quartiles[1] < b_x.domain()[1]*.10) {
return "#00a1b0";
//return "#bfbfbf"
}
else { return "#00a1b0";}
}
Insert cell
//reset to turn on labels
text_highlight = d => {
if (ecm_data.get(d).quartiles[1] < b_x.domain()[1]*.10) {
return 0;
}
else { return 0;}
//else { return 1;}
}
Insert cell
percentage_savings = d => {
const reduction = ecm_data.get(d).quartiles[1] / (data.map(d => d["GHG"]).reduce((a, b) => a + b)/ data.length / 2204);
return d3.format(".0%")(reduction)

}
Insert cell
t_x = d3.scaleBand()
.domain([0, 1])
.range([margin.left, width - margin.right])
.padding(0.1)
.paddingOuter(1)
Insert cell
t_y = d3.scaleLinear()
.domain([0, calcghg(data[0])/2204 * 1.3]).nice()
.range([height - margin.bottom, margin.top])
Insert cell
runs = ["Baseline (Current)", "With All Measures Combined"]
Insert cell
t_xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(t_x).tickFormat(i => runs[i]).tickSizeOuter(.5).tickPadding(12))
Insert cell
t_yAxis = g => g
.attr("transform", `translate(${margin.left},0)`).attr("class", "axis")
.call(d3.axisLeft(t_y).tickPadding(8).ticks(5).tickFormat(d3.format("~s")))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.html("Annual GHG Emissions MT CO2e"))
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