Published
Edited
Aug 11, 2021
1 fork
Importers
37 stars
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const dotPlot = Plot.plot({
width: width - boxWidth,
height: height,
x: { type: "time", domain: dateRange },
y: { grid: true, label: "usage (kW)", domain: dataRange },
marks: [
Plot.barY(
[{y1: st.max, y2: st.min}, {y1: st.upper, y2: st.lower}],
{y1: "y1", y2: "y2", fill: "darkkhaki", fillOpacity: 0.5}
),
Plot.dot(filtered, {x: "date", y: "usage", fill: "#4e79a7", title: formatTitle}),
Plot.text(
[filtered[d3.maxIndex(filtered, d => d.usage)], filtered[d3.minIndex(filtered, d => d.usage)]],
{x: "date", y: "usage", dx: "0.5em", text: "usage", textAnchor: "start", fill: "red", fontWeight: "bold"}
),
Plot.textY(
[{y: st.max, t: `${((range.high - range.low)*100).toFixed()}% of data point`}, {y: st.upper, t: "50% of data point"}],
{y: "y", dy: "1em", text: "t", fontWeight: "bold"}
)
]
});
const boxPlot = Plot.plot({
width: boxWidth,
height: height,
x: { type: "identity" },
y: { label: null , domain: dataRange},
marks: [
Plot.ruleX([{y1: st.min, y2: st.max}], {x: 12.5, y1: "y1", y2: "y2"}),
Plot.rect([{y1: st.lower, y2: st.upper}], {x1: 0, x2: 25, y1: "y1", y2: "y2", fill:"rgba(244,84,30,1)", stroke: "black", strokeWidth: 1}),
Plot.ruleY([st.min, st.max], {x1: 7, x2: 19}),
Plot.ruleY([{y: st.mean}], {x1: 0, x2: 25, y: "y", strokeWidth: 0.75}),
Plot.ruleY([{y: st.median}], {x1: 0, x2: 25, y: "y", strokeDasharray: "2", strokeWidth: 0.75}),
Plot.text(
[
{y: st.lower, t: `25th Percentile (${st.lower.toFixed(2)})`},
{y: st.upper, t: `75th Percentile (${st.upper.toFixed(2)})`},
{y: st.min, t: `${range.low*100}th Percentile (${st.min.toFixed(2)})`},
{y: st.max, t: `${range.high*100}th Percentile (${st.max.toFixed(2)})`},
{y: st.mean, t: `Mean (${st.mean.toFixed(2)})`},
{y: st.median, t: `Median (${st.median.toFixed(2)})`}
],
{x: 30, y: "y", text: "t", textAnchor: "start"}
),
Plot.dotY(
filtered.filter(d => d.usage < st.min || d.usage > st.max),
{x: 12.5, y: "usage", fill: "#999", fillOpacity: 0.5}
)
]
});
[...boxPlot.querySelectorAll("g.tick")].forEach(d => d.remove());
return html`<div style="display: flex">${[dotPlot, boxPlot]}</div>`;
}
Insert cell
formatTitle = d => `${d3.timeFormat("%Y/%m/%d")(d.date)}\n${d.usage.toFixed(2)} kW`
Insert cell
filtered = data.filter(d => d.hour === hour)
Insert cell
st = {
const values = filtered.map(d => d.usage)
return {
max: d3.quantile(values, range.high),
upper: d3.quantile(values, 0.75),
lower: d3.quantile(values, 0.25),
min: d3.quantile(values, range.low),
mean: d3.mean(values),
median: d3.median(values)
}
}
Insert cell
dateRange = d3.extent(data.map(d => d.date))
Insert cell
dataRange = d3.extent(data, d => d.usage)
Insert cell
hours = [...new Set(data.map(d => d.hour))]
Insert cell
data = {
const src = await FileAttachment("pge-electric-data.csv").csv();
return src.map(d => ({
date: d3.timeParse("%Y-%m-%d")(d.DATE),
hour: +d["START TIME"].substring(0, 2),
usage: +d.USAGE
}));
}
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