20 Commits

Author SHA1 Message Date
1993cb74d9 Improve readability of the code 2025-05-18 23:14:30 +02:00
50ae08855b Move update logic out of main code 2025-05-18 23:07:42 +02:00
dc55012963 Simplify code 2025-05-18 23:04:31 +02:00
156c32be67 Move inputs into one loop 2025-05-18 23:00:41 +02:00
6e4f6a5009 Update DGP + app 2025-05-18 22:57:12 +02:00
5337e434b3 Align input width and plot width 2025-05-18 22:45:13 +02:00
7a21162dc9 Update nonc input 2025-05-18 22:37:38 +02:00
ccd418a43a Update tailweight input 2025-05-18 22:24:03 +02:00
78b50f902a Fix sigma input range 2025-05-18 22:19:13 +02:00
1e8669ecc7 Update sigma input 2025-05-18 22:17:52 +02:00
f33e413893 Fix mu slider 2025-05-18 22:13:42 +02:00
f7de9da53a Update app.qmd (add inputs) 2025-05-18 22:11:57 +02:00
b3d5255898 Update app (controls) 2025-05-18 21:13:04 +02:00
9a2cef25df Adjust input size. 2025-05-18 21:07:24 +02:00
7a0c64676c Update app 2025-05-18 20:56:02 +02:00
f9f04f22b4 Simplify data basis 2025-05-18 01:58:20 +02:00
f768f88730 Completely fancify 2025-05-18 01:52:04 +02:00
6b304b4b32 Add nice transitions 2025-05-18 01:48:42 +02:00
87ee41b55d Make it d3 2025-05-18 01:13:04 +02:00
e26b20a017 Init simple web-app 2025-05-18 00:50:37 +02:00
2 changed files with 281 additions and 0 deletions

210
app.qmd Normal file
View File

@@ -0,0 +1,210 @@
---
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
---
```{ojs}
d3 = require("d3@7")
```
```{ojs}
bsplineData = FileAttachment("basis_functions.csv").csv({ typed: true })
```
```{ojs}
function updateChartInner(g, x, y, linesGroup, color, line, 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));
const keyFn = d => d[0];
// Update basis function lines
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);
}
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("max-width", "none")
.style("width", "100%");;
const controlsContainer = container.append("div")
.style("display", "flex")
.style("gap", "20px");
// slider controls
const sliders = [
{ label: 'Mu', get: () => selectedMu, set: v => selectedMu = v, min: 0.1, max: 0.9, step: 0.2 },
{ label: 'Sigma', get: () => Math.log2(selectedSig), set: v => selectedSig = 2 ** v, min: -2, max: 2, step: 1 },
{ label: 'Noncentrality', get: () => selectedNonc, set: v => selectedNonc = v, min: -4, max: 4, step: 2 },
{ label: 'Tailweight', get: () => Math.log2(selectedTailw), set: v => selectedTailw = 2 ** v, min: -2, max: 2, step: 1 }
];
// Build slider controls with D3 data join
const sliderCont = controlsContainer.selectAll('div').data(sliders).join('div')
.style('display','flex').style('align-items','center').style('gap','10px')
.style('flex','1').style('min-width','0px');
sliderCont.append('label').text(d => d.label + ':').style('font-size','20px');
sliderCont.append('input')
.attr('type','range').attr('min', d => d.min).attr('max', d => d.max).attr('step', d => d.step)
.property('value', d => d.get())
.on('input', function(event, d) {
const val = +this.value; d.set(val);
d3.select(this.parentNode).select('span').text(d.label.match(/Sigma|Tailweight/) ? 2**val : val);
updateChart(filteredData());
})
.style('width', '100%');
sliderCont.append('span').text(d => (d.label.match(/Sigma|Tailweight/) ? d.get() : d.get()))
.style('font-size','20px');
// 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;
// Set controls container width to match SVG plot width
controlsContainer.style("max-width", "none").style("width", "100%");
// Distribute each control evenly and make sliders full-width
controlsContainer.selectAll("div").style("flex", "1").style("min-width", "0px");
controlsContainer.selectAll("input").style("width", "100%").style("box-sizing", "border-box");
// 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", "100%")
.attr("height", "auto")
.attr("viewBox", [0, 0, width, height])
.attr("preserveAspectRatio", "xMidYMid meet")
.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", "20px")
// .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", "20px");
const yAxis = g.append("g")
.attr("class", "y-axis")
.call(d3.axisLeft(y).ticks(5))
.style("font-size", "20px");
// 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) {
updateChartInner(g, x, y, linesGroup, color, line, data);
}
// Store the update function
svg.node().update = updateChart;
// Initial render
updateChart(filteredData());
container.node().appendChild(svg.node());
return container.node();
}
```

71
test.R Normal file
View File

@@ -0,0 +1,71 @@
# %%
library(profoc)
library(ggplot2)
library(tidyr)
library(dplyr)
library(readr)
# Creating faceted plots for different knot values and mu values
# Create a function to generate the data for a given number of knots and mu value
generate_basis_data <- function(num_knots, mu_value, sig_value, nonc_value, tailw_value, deg_value) {
grid <- seq(from = 0.01, to = 0.99, length.out = 50)
# Use provided degree
B <- profoc:::make_basis_matrix(grid,
profoc::make_knots(
n = num_knots,
mu = mu_value,
sig = sig_value,
nonc = nonc_value,
tailw = tailw_value,
deg = deg_value
),
deg = deg_value
)
B_mat <- round(as.matrix(B),2)
df <- as.data.frame(B_mat)
df$x <- round(grid,2)
df_long <- pivot_longer(df,
cols = -x,
names_to = "b",
values_to = "y"
)
df_long$knots <- num_knots
df_long$mu <- mu_value
df_long$sig <- sig_value
df_long$nonc <- nonc_value
df_long$tailw <- tailw_value
df_long$deg <- deg_value
return(df_long)
}
# Generate data for each combination of knot, mu, sig, nonc, tailw, and deg
knot_values <- c(10)
mu_values <- seq(0.1, 0.9, by = 0.2)
sig_values <- 2^(-2:2)
nonc_values <- c(-4,-2,0,2,4)
tailw_values <- 2^(-2:2)
# Create an empty list to store all combinations
all_data <- list()
counter <- 1
# Nested loops to cover all parameter combinations
for (k in knot_values) {
print(paste("Processing knots:", k))
for (m in mu_values) {
print(paste("Processing mu:", m))
for (s in sig_values) {
for (nc in nonc_values) {
for (tw in tailw_values) {
all_data[[counter]] <- generate_basis_data(5, m, s, nc, tw, 2)
counter <- counter + 1
}
}
}
}
}
# Combine all data frames
all_data <- bind_rows(all_data)
write_csv(all_data, "basis_functions.csv")