Published
Edited
Apr 20, 2021
2 forks
Importers
Insert cell
md`# Google 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
b_margin = ({top: 80, right: 100, bottom: 50, left: 280})
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
ecm_data = {
// const filtered = data.filter(d => d.Run != "Metered" && d.Run != "Baseline");
// const grouped = _.groupBy(filtered, d=> d.Run);

const keys = d3.range(1, 13).map(d => [`E${d}`, `H${d}`]).flat();
const map = new Map();

let baseline = wb.Sheets["Baseline"];
let baselineDat = xlsx.utils.sheet_to_json(baseline);
for (const key of Object.keys(b_lookup)) {
let excel = wb.Sheets[key];
let dat = xlsx.utils.sheet_to_json(excel);
let savings = dat.map((d, i) => {
let b = d3.sum(keys.map(k => baselineDat[i][k]));
let a = d3.sum(keys.map(k => d[k]));

return Math.max(b -a, 0);
});
map.set(key, quartiles(savings));
}

return map;
}
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": "Building Envelope",
"LPD": "Lighting System",
"HRV": "Energy Recovery",
// "VENT2": "Ventilation Measures",
// "VENT_Reduced": "Ventilation Air Reductions",
"VentilationControl": "Ventilation Controls",
"FanPower": "Fan System",
"STR60": "Supply Air Temperature Reset",
"Combined": "All EEMs Combined",
"AllElectric": "All Electric - No EEMs",
"Combined_AllElectric": "All Electric - All EEMs 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 Site Energy Reductions (kbtu/ft&sup2)"))
.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_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 / 2000);
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, data.map(d => d["GHG"]).reduce((a, b) => a + b)/ data.length / 2000 * 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 Tons 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