Public
Edited
Mar 14
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Plot.plot({
width,
marginLeft: 60,
y: {
grid: true,
label: "↑ Demand (megawatthours)"
},
marks: [
Plot.lineY(ba_data, {x: "date", y: metricSelect, strokeOpacity: 0.15}),
Plot.lineY(ba_data, Plot.windowY({k: 24, reduce: "min"}, {x: "date", y: metricSelect, strokeOpacity: 0.5, strokeWidth: 1})),
Plot.lineY(ba_data, Plot.windowY({k: 24, reduce: "max"}, {x: "date", y: metricSelect, strokeOpacity: 0.5, strokeWidth: 1})),
Plot.lineY(ba_data, Plot.windowY({k: 24, reduce: "median"}, {x: "date", y: metricSelect}))
]
})
Insert cell
Insert cell
Insert cell
Insert cell
// viewof metricSelect = Inputs.select([...d3.union(data.map(d => d.name))], {label: "Select Calendar Display Metric:"})
Insert cell
Insert cell
Insert cell
Plot.plot({
width,
marginLeft: 60,
padding: 0,
x: {grid: true},
fy: {grid: true, domain: d3.groupSort(data_crossvis.filter((d) => d.net_generation), (g) => d3.median(g, (d) => d.net_generation), (d) => d.ba)},
color: {scheme: "BuPu", type: "quantize", legend: true},
marks: [Plot.rect(data_crossvis, Plot.binX({fill: "proportion-facet"}, {x: "net_generation", fy: "ba", inset: 0.5, strokeOpacity: 0.1, strokeWidth: 1}))]
})
Insert cell
Insert cell
ba_data = data_crossvis.filter(d => d.ba === baSelect);
Insert cell
Plot.rect(ba_data, Plot.bin({fill: "count"}, {x: "net_generation", y: "demand_forecast"})).plot({marginLeft: 80, grid: true, color: {scheme: "BuPu", type: "symlog", legend: true}})
Insert cell
Plot.rect(ba_data, Plot.bin({fill: "count"}, {x: "demand", y: "demand_forecast"})).plot({marginLeft: 80, grid: true, color: {scheme: "BuPu", type: "symlog", legend: true}})
Insert cell
Insert cell
d3.groups(data, d => d.date.getUTCHours(), d => d.date.getMonth())

Insert cell
hour_summary = {
return d3.merge(d3.groups(data, d => d.date.getUTCHours(), d => d.date.getMonth()).map(hr => {
return hr[1].map(mth => {
const values = mth[1].filter(d => d.name === metricSelect && d.ba === baSelect).map(d => d.value).sort(d3.ascending);
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;
const r0 = Math.max(min, q1 - iqr * 1.5);
const r1 = Math.min(max, q3 + iqr * 1.5);
return {
month: mth[0],
hour: hr[0],
values, min, max, q1, q2, q3, iqr, r0, r1,
quartiles: [q1, q2, q3],
range: [r0, r1],
outliers: values.filter(v => v < r0 || v > r1)
};
}).sort((a,b) => d3.ascending(a.hour, b.hour));
})).sort((a,b) => {
if (a.month === b.month) {
return a.hour - b.hour;
} else {
return a.month - b.month
}
// d3.ascending(a.month, b.month)
});
// return d3.groups(data, d => d.date.getUTCHours(), d => d.date.getMonth()).map(d => {
// const values = d[1].filter(d => d.name === metricSelect && d.ba === baSelect).map(d => d.value).sort(d3.ascending);
// 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;
// const r0 = Math.max(min, q1 - iqr * 1.5);
// const r1 = Math.min(max, q3 + iqr * 1.5);
// return {
// hour: d[0],
// values, min, max, q1, q2, q3, iqr, r0, r1,
// quartiles: [q1, q2, q3],
// range: [r0, r1],
// outliers: values.filter(v => v < r0 || v > r1)
// };
// }).sort((a,b) => d3.ascending(a.hour, b.hour))
}
Insert cell
Insert cell
data = {
let raw_data = await FileAttachment("EIA930_BALANCE_2023_Jan_Jun.csv").csv();
raw_data = raw_data.concat(await FileAttachment("EIA930_BALANCE_2023_Jul_Dec (1).csv").csv());
raw_data = raw_data.concat(await FileAttachment("EIA930_BALANCE_2024_Jan_Jun.csv").csv());
// const raw_data = await FileAttachment("EIA930_BALANCE_2023_Jul_Dec (1).csv").csv();
// return raw_data;

const utcParse = d3.utcParse("%m/%d/%Y %-I:%M:%S %p");
const localParse = d3.timeParse("%m/%d/%Y %-I:%M:%S %p");
return d3.merge(raw_data.map(d => {
const date = utcParse(d["UTC Time at End of Hour"]);
return [
{
date,
ba: d["Balancing Authority"],
region: d["Region"],
name: "demand_forecast",
value: +d["Demand Forecast (MW)"].replace(",",""),
},
{
date,
ba: d["Balancing Authority"],
region: d["Region"],
name: "demand",
value: +d["Demand (MW)"].replace(",",""),
},
{
date,
ba: d["Balancing Authority"],
region: d["Region"],
name: "net_generation",
value: +d["Net Generation (MW)"].replace(",",""),
},
];
// return {
// region: d["Region"],
// ba: d["Balancing Authority"],
// // local_date: localParse(d["Local Time at End of Hour"]),
// // local_date_str: d["Local Time at End of Hour"],
// date: utcParse(d["UTC Time at End of Hour"]),
// // utc_date_str: d["UTC Time at End of Hour"],
// hour: +d["Hour Number"],
// demand_forecast: +d["Demand Forecast (MW)"].replace(",",""),
// // demand_forecast_str: d["Demand Forecast (MW)"],
// demand: +d["Demand (MW)"].replace(",",""),
// net_generation: +d["Net Generation (MW)"].replace(",",""),
// total_interchange: +d["Total Interchange (MW)"].replace(",", ""),
// sum: +d["Sum(Valid DIBAs) (MW)"].replace(",", ""),
// deman_adj: +d["Demand (MW) (Adjusted)"].replace(",", ""),
// net_generation_adj: +d["Net Generation (MW) (Adjusted)"].replace(",", ""),
// total_interchange_adj: +d["Total Interchange (MW) (Adjusted)"].replace(",", ""),
// };
}));
}
Insert cell
EIA930_BALANCE_2024_Jan_Jun.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
data_crossvis = {
let raw_data = await FileAttachment("EIA930_BALANCE_2023_Jan_Jun.csv").csv();
raw_data = raw_data.concat(await FileAttachment("EIA930_BALANCE_2023_Jul_Dec (1).csv").csv());

const utcParse = d3.utcParse("%m/%d/%Y %-I:%M:%S %p");
const localParse = d3.timeParse("%m/%d/%Y %-I:%M:%S %p");
return raw_data.map(d => {
return {
region: d["Region"],
ba: d["Balancing Authority"],
// local_date: localParse(d["Local Time at End of Hour"]),
// local_date_str: d["Local Time at End of Hour"],
date: utcParse(d["UTC Time at End of Hour"]),
// utc_date_str: d["UTC Time at End of Hour"],
hour: +d["Hour Number"],
demand_forecast: +d["Demand Forecast (MW)"].replace(",",""),
// demand_forecast_str: d["Demand Forecast (MW)"],
demand: +d["Demand (MW)"].replace(",",""),
net_generation: +d["Net Generation (MW)"].replace(",",""),
total_interchange: +d["Total Interchange (MW)"].replace(",", ""),
sum: +d["Sum(Valid DIBAs) (MW)"].replace(",", ""),
deman_adj: +d["Demand (MW) (Adjusted)"].replace(",", ""),
net_generation_adj: +d["Net Generation (MW) (Adjusted)"].replace(",", ""),
total_interchange_adj: +d["Total Interchange (MW) (Adjusted)"].replace(",", ""),
};
});
}
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