Published
Edited
May 18, 2021
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// md`</br></br>
// ### Recommendations and Next Steps
// Coming soon!</br></br>`
Insert cell
Insert cell
line = d3.line()
// .defined(d => !isNaN(d))
.curve(d3.curveBundle.beta(1))
.x((d, i) => distribution_x(distribution_data.costs[i]))
.y(d => distribution_y(d))
Insert cell
distribution_x = d3.scaleLinear()
.domain(d3.extent(distribution_range))
.range([distribution_margin.left, distribution_dimensions.width - distribution_margin.right]).nice()
Insert cell
distribution_margin = ({top: 70, right: 20, bottom: 60, left: 30})
Insert cell
distribution_dimensions = ({width: 960, height: 550})
Insert cell
distribution_xAxis = g => g
.attr("transform", `translate(0,${distribution_dimensions.height - distribution_margin.bottom})`)
.call(d3.axisBottom(distribution_x).tickPadding(6).ticks(20, ".1s"))
.call(g => g.append("text")
.attr("x", distribution_dimensions.width - 10)
.attr("y", 45)
.attr("fill", "currentColor")
.attr("text-anchor", "end")
.text("Minimum Cost Savings ($)"))
Insert cell
distribution_y = d3.scaleLinear()
.domain([0, 1])
.range([distribution_dimensions.height - distribution_margin.bottom, distribution_margin.top]).nice()
Insert cell
color(rate_selection)
Insert cell
distribution_yAxis = g => g
.attr("transform", `translate(${distribution_margin.left},0)`)
.call(d3.axisLeft(distribution_y).tickPadding(6).ticks(10, "%"))
.call(g => g.select(".domain").remove())
.call(g => g.append("text")
.attr("x", -distribution_margin.left - 10)
.attr("y", distribution_margin.top - 25)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text(distribution_data.y))
Insert cell
distribution_grid = g => g
.attr("stroke", "currentColor")
.attr("stroke-opacity", 0.1)
.call(g => g.append("g")
.selectAll("line")
.data(distribution_y.ticks())
.join("line")
.attr("y1", d => 0.5 + distribution_y(d))
.attr("y2", d => 0.5 + distribution_y(d))
.attr("x1", distribution_margin.left)
.attr("x2", distribution_dimensions.width - distribution_margin.right));
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
color = d => d3.scaleOrdinal(rates, ["#be4579", "#89cda8", "#fcd404", "#3b99a7", "#e26e42", "#c59fca"])(d)
Insert cell
Insert cell
Insert cell
grid = g => g
.attr("stroke", "currentColor")
.attr("stroke-opacity", 0.1)
.call(g => g.append("g")
.selectAll("line")
.data(scatterplot_x.ticks())
.join("line")
.attr("x1", d => 0.5 + scatterplot_x(d))
.attr("x2", d => 0.5 + scatterplot_x(d))
.attr("y1", scatterplot_margin.top)
.attr("y2", scatterplot_dimensions.height - scatterplot_margin.bottom))
.call(g => g.append("g")
.selectAll("line")
.data(scatterplot_y.ticks())
.join("line")
.attr("y1", d => 0.5 + scatterplot_y(d))
.attr("y2", d => 0.5 + scatterplot_y(d))
.attr("x1", scatterplot_margin.left)
.attr("x2", scatterplot_dimensions.width - scatterplot_margin.right));
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
scatterplot_yAxis = g => g
.attr("transform", `translate(${scatterplot_margin.left},0)`)
.call(d3.axisLeft(scatterplot_y).tickPadding(6).ticks(20, ".1s"))
.call(g => g.select(".domain").remove())
.call(g => g.append("text")
.attr("x", -scatterplot_margin.left)
.attr("y", scatterplot_margin.top - 25)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text(data.y))
Insert cell
Insert cell
distribution_legend = g => {
const svg = g
.attr("transform", `translate(${distribution_dimensions.width - distribution_margin.right},10)`)
.attr("font-family", "sans-serif")
.attr("font-size", 12)
.attr("text-anchor", "start");
const item = svg
.selectAll("g")
.data(["BATTERY + PV", "PV ONLY"])
.join("g")
.attr("transform", (d, i) => `translate(0,${i * 25})`);
item.each(function(d, i) {
d3.select(this).append("text")
.attr("fill", "#c1c1c1")
.attr("x", -110)
.attr("dy", "0.35em")
.text(String);
d3.select(this).append("circle")
.attr("fill", i === 0 ? color(rate_selection): "#c6c6c6")
.attr("stroke-width", 0)
.attr("r", 4)
.attr("cx", -125)
.attr("cy", 0);
});
}

Insert cell
Insert cell
grouped = {
let dat = data.filter(d => d.rate === rate_selection);
let grouped = _.groupBy(dat, d => d.id);
for (const group in grouped) {
grouped[group] = {
base: quartiles(grouped[group], "base_cost"),
ppa: quartiles(grouped[group], "ppa_with_battery_cost")
}
}
for (const bid in bids) {
let k = `${bid}PPA`
let d = dat.filter(d => d.id.substr(0, 2) === bid);
grouped[k] = {
base: quartiles(d, "base_cost"),
ppa: quartiles(d, "ppa_pv_only_cost")
}
}
return Object.entries(grouped).sort((a, b) => b[1].ppa[2] - a[1].ppa[2]);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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