Files
PHD-Presentation/app.qmd
2025-05-18 22:37:38 +02:00

227 lines
7.6 KiB
Plaintext

---
title: "Data Science Methods for Forecasting in Energy and Economics"
date: 2025-07-10
format:
revealjs:
embed-resources: true
footer: ""
execute:
daemon: false
highlight-style: github
---
## B-spline Basis Functions
```{ojs}
d3 = require("d3@7")
```
```{ojs}
bsplineData = FileAttachment("basis_functions.csv").csv({ typed: true })
muValues = Array.from(new Set(bsplineData.map(d => d.mu))).sort((a, b) => a - b)
minMu = Math.min(...muValues)
maxMu = Math.max(...muValues)
```
```{ojs}
chart = {
// State variables for selected parameters
let selectedMu = 0.5;
let selectedSig = 1;
let selectedNonc = 0;
let selectedTailw = 1;
const filteredData = () => bsplineData.filter(d =>
Math.abs(selectedMu - d.mu) < 0.001 &&
d.sig === selectedSig &&
d.nonc === selectedNonc &&
d.tailw === selectedTailw
);
const container = d3.create("div").style("margin-bottom", "20px");
const controlsContainer = container.append("div")
.style("display", "flex")
.style("gap", "20px");
// μ slider control
const muControl = controlsContainer.append("div").style("display","flex").style("align-items","center").style("gap","10px");
muControl.append("label").text("μ:").style("font-size","16px");
muControl.append("input")
.attr("type","range").attr("min",minMu).attr("max",maxMu).attr("step",0.2)
.property("value",selectedMu)
.on("input", function() { selectedMu = +this.value; muControl.select("span").text(selectedMu); updateChart(filteredData()); });
muControl.append("span").text(selectedMu).style("font-size","16px");
// sigma control
const sigControl = controlsContainer.append("div").style("display","flex").style("align-items","center").style("gap","10px");
sigControl.append("label").text("Sigma:").style("font-size","16px");
sigControl.append("input")
.attr("type","range").attr("min", -2).attr("max", 2).attr("step", 1)
.property("value", Math.log2(selectedSig))
.on("input", function() { selectedSig = 2 ** (+this.value); sigControl.select("span").text(selectedSig); updateChart(filteredData()); });
sigControl.append("span").text(selectedSig).style("font-size","16px");
// nonc control
const noncControl = controlsContainer.append("div").style("display","flex").style("align-items","center").style("gap","10px");
noncControl.append("label").text("Non-centrality:").style("font-size","16px");
noncControl.append("input")
.attr("type","range").attr("min", -4).attr("max", 4).attr("step", 2)
.property("value", selectedNonc)
.on("input", function() { selectedNonc = +this.value; noncControl.select("span").text(selectedNonc); updateChart(filteredData()); });
noncControl.append("span").text(selectedNonc).style("font-size","16px");
// tail weight control
const tailwControl = controlsContainer.append("div").style("display","flex").style("align-items","center").style("gap","10px");
tailwControl.append("label").text("Tail weight:").style("font-size","16px");
tailwControl.append("input")
.attr("type","range").attr("min", -2).attr("max", 2).attr("step", 1)
.property("value", Math.log2(selectedTailw))
.on("input", function() { selectedTailw = 2 ** (+this.value); tailwControl.select("span").text(selectedTailw); updateChart(filteredData()); });
tailwControl.append("span").text(selectedTailw).style("font-size","16px");
// Build SVG
const width = 800;
const height = 400;
const margin = {top: 40, right: 20, bottom: 40, left: 40};
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
// Create scales
const x = d3.scaleLinear()
.domain([0, 1])
.range([0, innerWidth]);
const y = d3.scaleLinear()
.domain([0, 0.7])
.range([innerHeight, 0]);
// Create a color scale for the basis functions
const color = d3.scaleOrdinal(d3.schemeCategory10);
// Create SVG
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto;");
// Add chart title
// svg.append("text")
// .attr("class", "chart-title")
// .attr("x", width / 2)
// .attr("y", 20)
// .attr("text-anchor", "middle")
// .attr("font-size", "16px")
// .attr("font-weight", "bold");
// Create the chart group
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// Add axes
const xAxis = g.append("g")
.attr("transform", `translate(0,${innerHeight})`)
.attr("class", "x-axis")
.call(d3.axisBottom(x).ticks(10))
.style("font-size", "16px");
const yAxis = g.append("g")
.attr("class", "y-axis")
.call(d3.axisLeft(y).ticks(5))
.style("font-size", "16px");
// Add axis labels
// g.append("text")
// .attr("x", innerWidth / 2)
// .attr("y", innerHeight + 35)
// .attr("text-anchor", "middle")
// .text("x");
// g.append("text")
// .attr("transform", "rotate(-90)")
// .attr("x", -innerHeight / 2)
// .attr("y", -30)
// .attr("text-anchor", "middle")
// .text("y");
// Add a horizontal line at y = 0
g.append("line")
.attr("x1", 0)
.attr("x2", innerWidth)
.attr("y1", y(0))
.attr("y2", y(0))
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2);
// Add gridlines
g.append("g")
.attr("class", "grid-lines")
.selectAll("line")
.data(y.ticks(5))
.join("line")
.attr("x1", 0)
.attr("x2", innerWidth)
.attr("y1", d => y(d))
.attr("y2", d => y(d))
.attr("stroke", "#ccc")
.attr("stroke-opacity", 0.5);
// Create a line generator
const line = d3.line()
.x(d => x(d.x))
.y(d => y(d.y))
.curve(d3.curveBasis);
// Group to contain the basis function lines
const linesGroup = g.append("g")
.attr("class", "basis-functions");
// Store the current basis functions for transition
let currentBasisFunctions = new Map();
// Function to update the chart with new data
function updateChart(data) {
// Update axes with transitions
x.domain([0, d3.max(data, d => d.x)]);
g.select(".x-axis")
.transition().duration(1500)
.call(d3.axisBottom(x).ticks(10));
y.domain([0, d3.max(data, d => d.y)]);
g.select(".y-axis")
.transition().duration(1500)
.call(d3.axisLeft(y).ticks(5));
// Group data by basis function
const dataByFunction = Array.from(d3.group(data, d => d.b));
// Update the chart title
// svg.select(".chart-title")
// .text(`B-spline Basis Functions (${selectedKnots} knots, μ = ${selectedMu})`);
// Create a key function to track basis functions
const keyFn = d => d[0];
// Update basis function lines with proper enter/update/exit pattern
const u = linesGroup.selectAll("path")
.data(dataByFunction, keyFn);
u.join(
enter => enter.append("path")
.attr("fill", "none")
.attr("stroke-width", 3)
.attr("stroke", (_, i) => color(i))
.attr("d", d => line(d[1].map(pt => ({x: pt.x, y: 0}))))
.style("opacity", 0),
update => update,
exit => exit.transition().duration(1000).style("opacity", 0).remove()
)
.transition().duration(1000)
.attr("d", d => line(d[1]))
.attr("stroke", (_, i) => color(i))
.style("opacity", 1);
}
// Store the update function
svg.node().update = updateChart;
// Initial render
updateChart(filteredData());
container.node().appendChild(svg.node());
return container.node();
}
```