---
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 })

knotValues = Array.from(new Set(bsplineData.map(d => d.knots))).sort((a, b) => a - b)
minKnots = Math.min(...knotValues)
maxKnots = Math.max(...knotValues)

muValues = Array.from(new Set(bsplineData.map(d => d.mu))).sort((a, b) => a - b)
minMu = Math.min(...muValues)
maxMu = Math.max(...muValues)

// Create a more compact layout for controls
viewof controls = Inputs.form({
  knots: Inputs.range([minKnots, maxKnots], {value: minKnots, step: 1, label: "Knots:", width: 200}),
  mu: Inputs.range([minMu, maxMu], {value: 0.5, step: 0.1, label: "μ:", width: 200})
}, {
  submit: false,
  layout: 'horizontal',
  style: 'display: flex; gap: 20px; align-items: center; margin-bottom: 10px; font-size: 0.9em;'
})

selectedKnots = controls.knots
selectedMu = controls.mu

filteredBspline = bsplineData.filter(function(row) {
  return selectedKnots == row.knots && Math.abs(selectedMu - row.mu) < 0.001;
})
```

```{ojs}
// D3-based visualization for B-spline basis functions
chart = {
  // Create chart dimensions
  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})`)
    .call(d3.axisBottom(x).ticks(10));
  
  const yAxis = g.append("g")
    .call(d3.axisLeft(y).ticks(5));
  
  // 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));
  
  // Group to contain the basis function lines
  const linesGroup = g.append("g")
    .attr("class", "basis-functions");
  
  // Function to update the chart with new data
  function updateChart(data) {
    // Group data by basis function
    const groupedData = d3.group(data, d => d.b);
    
    // Update the chart title
    svg.select(".chart-title")
      .text(`B-spline Basis Functions (${selectedKnots} knots, μ = ${selectedMu})`);
    
    // Update or create paths
    const paths = linesGroup.selectAll("path")
      .data(Array.from(groupedData.values()));
    
    // Remove paths that are no longer needed
    paths.exit().remove();
    
    // Add new paths
    paths.enter()
      .append("path")
      .attr("fill", "none")
      .attr("stroke-width", 3)
      .attr("stroke", (_, i) => color(i))
      .merge(paths) // Merge with existing paths for transition
      .transition()
      .duration(750)
      .attr("d", line);
  }
  
  // Store the update function
  svg.node().update = updateChart;
  
  // Initial render
  updateChart(filteredBspline);
  
  return svg.node();
}
```
