Public
Edited
May 13, 2024
Importers
Insert cell
Insert cell
Insert cell
dataCnnLSTM = FileAttachment("CnnLSTMloss_function@2.csv").csv({typed: true})
Insert cell
dataConvLSTM = FileAttachment("ConvLSTMloss_function.csv").csv({typed: true})
Insert cell
dataCnnLSTM_50 = d3.filter(dataCnnLSTM, d => d.time < 50)
Insert cell
dataConvLSTM_50 = d3.filter(dataConvLSTM, d => d.time < 50)
Insert cell
dataLSTM = FileAttachment("LSTMloss_function@1.csv").csv({typed: true})
Insert cell
dataLSTM_50 = d3.filter(dataLSTM, d => d.time < 50)
Insert cell
dataCNN = FileAttachment("CNNloss_function.csv").csv({typed: true})
Insert cell
dataCNN_50 = d3.filter(dataCNN, d => d.time < 50)
Insert cell
dataMLP = FileAttachment("MLPloss_function@1.csv").csv({typed: true})
Insert cell
dataMLP_50 = d3.filter(dataMLP, d => d.time < 50)
Insert cell
width = 600
Insert cell
height = 800
Insert cell
chartWidth = width
Insert cell
chartHeight = height / 5
Insert cell
legendData = [
{index: 1, label: "Train loss"},
{index: 2, label: "Val loss"}
]
Insert cell
{
const svg = d3.create('svg')
.attr('width', 200)
.attr('height', 110);
const legend = svg.append('g')
.attr('transform', 'translate(0, 10)')
.call(colorLegend); // <-- our legend helper is invoked just like an axis generator

return svg.node();
}
Insert cell
function colorLegend(container) {
// Also explain we decided on these constants because they look nice
const titlePadding = 0; // padding between title and entries
const entrySpacing = 16; // spacing between legend entries
const entryRadius = 5; // radius of legend entry marks
const labelOffset = 6; // additional horizontal offset of text labels
const baselineOffset = 4; // text baseline offset, depends on radius and font size

const title = container
.append("text")
.text("Legend")
.attr("x", 0)
.attr("y", 0)
.attr("fill", "black")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-weight", "bold")
.attr("font-size", "10px");

const entries = container
.selectAll("g")
.data(legendData)
.join("g")
// we want to organize the <g>s in rows
.attr(
"transform",
(d) => `translate(0, ${titlePadding + d.index * entrySpacing})`
);


const line = entries.append("line")
.attr("x1", entryRadius)
.attr("x2", entryRadius * 4)
.attr("y1", 0)
.attr("y2", 0)
.attr("stroke", (d) => d.index == 1? "teal" : "#E4572E")
.attr("stroke-width", "2px")

const labels = entries
.append("text")
.text((d) => d.label)
.attr("x", 4 * entryRadius + labelOffset) // to the right of symbols
.attr("y", baselineOffset) // alignment adjustment
.attr("fill", "black")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-size", "10px")
}
Insert cell
allCharts = {
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
const subPlot1 = svg.append("g")
.attr("class", "mlp");

const sublot2 = svg.append("g")
.attr("transform", `translate(0, ${chartHeight})`)
.attr("class", "cnn");

const sublot3 = svg.append("g")
.attr("transform", `translate(0, ${chartHeight * 2})`)
.attr("class", "lstm");
const sublot4 = svg.append("g")
.attr("transform", `translate(0, ${chartHeight * 3})`)
.attr("class", "cnnlstm");

const sublot5 = svg.append("g")
.attr("transform", `translate(0, ${chartHeight * 4})`)
.attr("class", "convlstm");
return svg.node();
}
Insert cell
margins = ({top: 40, right: 10, bottom: 50, left: 40})
Insert cell
runchart = {
const chartsContainer = d3.select(allCharts)

chartsContainer.select("g.mlp").call(subPlot1)
chartsContainer.select("g.cnn").call(subPlot2)
chartsContainer.select("g.lstm").call(subPlot3)
chartsContainer.select("g.cnnlstm").call(subPlot4)
chartsContainer.select("g.convlstm").call(subPlot5)

return chartsContainer.node()
}
Insert cell
subPlot1 = svg => {

svg.selectAll("*").remove();

const margins = ({
top: 5,
right: 10,
bottom: 25,
left: 130
})

// Declare the x (horizontal position) scale.
const x = d3.scaleLinear(d3.extent(dataMLP_50, d => d.time), [margins.left, width - margins.right])

// Declare the y (vertical position) scale.
const y = d3.scaleLinear()
.domain([0.075, 0.13]).nice()
.range([chartHeight - margins.bottom, margins.top])

// Add and position the legend; place in the upper right corner.
const legend = svg
.append("g")
.attr("transform", `translate(${width - 80}, 15)`)
.call((container) => colorLegend(container))

const lineTrain = d3.line()
.x(d => x(d.time))
.y(d => y(d.train))

const lineVal = d3.line()
.x(d => x(d.time))
.y(d => y(d.val))

svg.append("g")
.attr("transform", `translate(0,${chartHeight - margins.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80))
.call(g => g.select(".domain").remove())
.call((g) => g.append("text")
.attr("x", width - margins.right)
.attr("y", margins.bottom - 10)
.attr("fill", "currentColor")
.attr("text-anchor", "end")
.attr("font-weight", "bold")
.text("epoch"))

svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line")
.clone()
.attr("x2", width - margins.right - margins.left)
.attr("stroke-opacity", d => d === 0 ? 1 : 0.1))
.call(g => g.append("text")
.attr("fill", "#000")
.attr("x", 5)
.attr("y", margins.top)
.attr("dy", "0.32em")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("mean absolute error"))

// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "teal")
.attr("stroke-width", 2)
.attr("d", lineTrain(dataMLP_50))

// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "#E4572E")
.attr("stroke-width", 2)
.attr("d", lineVal(dataMLP_50))

// Add a title
const title = svg.append("g")
.append("text")
.attr("x", margins.left / 3)
.attr("y", (chartHeight / 2))
.attr("text-anchor", "middle")
.attr("front-weight", "bold")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-size", "15px")
.text("MLP")
return svg.node();
}
Insert cell
subPlot2 = svg => {

svg.selectAll("*").remove();

const margins = ({
top: 5,
right: 10,
bottom: 25,
left: 130
})

// Declare the x (horizontal position) scale.
const x = d3.scaleLinear(d3.extent(dataCNN_50, d => d.time), [margins.left, width - margins.right])

// Declare the y (vertical position) scale.
const y = d3.scaleLinear()
.domain([0.075, 0.13]).nice()
.range([chartHeight - margins.bottom, margins.top])

const lineTrain = d3.line()
.x(d => x(d.time))
.y(d => y(d.train))

const lineVal = d3.line()
.x(d => x(d.time))
.y(d => y(d.val))

svg.append("g")
.attr("transform", `translate(0,${chartHeight - margins.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80))
.call(g => g.select(".domain").remove())
.call((g) => g.append("text")
.attr("x", width - margins.right)
.attr("y", margins.bottom - 10)
.attr("fill", "currentColor")
.attr("text-anchor", "end")
.attr("font-weight", "bold")
.text("epoch"))

svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line")
.clone()
.attr("x2", width - margins.right - margins.left)
.attr("stroke-opacity", d => d === 0 ? 1 : 0.1))
.call(g => g.append("text")
.attr("fill", "#000")
.attr("x", 5)
.attr("y", margins.top)
.attr("dy", "0.32em")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("mean absolute error"))

// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "teal")
.attr("stroke-width", 2)
.attr("d", lineTrain(dataCNN_50))

// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "#E4572E")
.attr("stroke-width", 2)
.attr("d", lineVal(dataCNN_50))

// Add a title
const title = svg.append("g")
.append("text")
.attr("x", margins.left / 3)
.attr("y", (chartHeight / 2))
.attr("text-anchor", "middle")
.attr("front-weight", "bold")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-size", "15px")
.text("CNN")
return svg.node();
}
Insert cell
subPlot3 = svg => {

svg.selectAll("*").remove();

const margins = ({
top: 5,
right: 10,
bottom: 25,
left: 130
})

// Declare the x (horizontal position) scale.
const x = d3.scaleLinear(d3.extent(dataLSTM_50, d => d.time), [margins.left, width - margins.right])

// Declare the y (vertical position) scale.
const y = d3.scaleLinear()
.domain([0.075, 0.13]).nice()
.range([chartHeight - margins.bottom, margins.top])

const lineTrain = d3.line()
.x(d => x(d.time))
.y(d => y(d.train))

const lineVal = d3.line()
.x(d => x(d.time))
.y(d => y(d.val))

svg.append("g")
.attr("transform", `translate(0,${chartHeight - margins.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80))
.call(g => g.select(".domain").remove())
.call((g) => g.append("text")
.attr("x", width - margins.right)
.attr("y", margins.bottom - 10)
.attr("fill", "currentColor")
.attr("text-anchor", "end")
.attr("font-weight", "bold")
.text("epoch"))

svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line")
.clone()
.attr("x2", width - margins.right - margins.left)
.attr("stroke-opacity", d => d === 0 ? 1 : 0.1))
.call(g => g.append("text")
.attr("fill", "#000")
.attr("x", 5)
.attr("y", margins.top)
.attr("dy", "0.32em")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("mean absolute error"))

// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "teal")
.attr("stroke-width", 2)
.attr("d", lineTrain(dataLSTM_50))

// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "#E4572E")
.attr("stroke-width", 2)
.attr("d", lineVal(dataLSTM_50))

// Add a title
const title = svg.append("g")
.append("text")
.attr("x", margins.left / 3)
.attr("y", (chartHeight / 2))
.attr("text-anchor", "middle")
.attr("front-weight", "bold")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-size", "15px")
.text("LSTM")
return svg.node();
}
Insert cell
subPlot4 = svg => {

svg.selectAll("*").remove();

const margins = ({
top: 5,
right: 10,
bottom: 25,
left: 130
})

// Declare the x (horizontal position) scale.
const x = d3.scaleLinear(d3.extent(dataCnnLSTM_50, d => d.time), [margins.left, width - margins.right])

// Declare the y (vertical position) scale.
const y = d3.scaleLinear()
.domain([0.075, 0.13]).nice()
.range([chartHeight - margins.bottom, margins.top])

const lineTrain = d3.line()
.x(d => x(d.time))
.y(d => y(d.train))

const lineVal = d3.line()
.x(d => x(d.time))
.y(d => y(d.val))

svg.append("g")
.attr("transform", `translate(0,${chartHeight - margins.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80))
.call(g => g.select(".domain").remove())
.call((g) => g.append("text")
.attr("x", width - margins.right)
.attr("y", margins.bottom - 10)
.attr("fill", "currentColor")
.attr("text-anchor", "end")
.attr("font-weight", "bold")
.text("epoch"))

svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line")
.clone()
.attr("x2", width - margins.right - margins.left)
.attr("stroke-opacity", d => d === 0 ? 1 : 0.1))
.call(g => g.append("text")
.attr("fill", "#000")
.attr("x", 5)
.attr("y", margins.top)
.attr("dy", "0.32em")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("mean absolute error"))

// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "teal")
.attr("stroke-width", 2)
.attr("d", lineTrain(dataCnnLSTM_50))

// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "#E4572E")
.attr("stroke-width", 2)
.attr("d", lineVal(dataCnnLSTM_50))

// Add a title
const title = svg.append("g")
.append("text")
.attr("x", margins.left / 3)
.attr("y", (chartHeight / 2))
.attr("text-anchor", "middle")
.attr("front-weight", "bold")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-size", "15px")
.text("CNN-LSTM")
return svg.node();
}
Insert cell
subPlot5 = svg => {

svg.selectAll("*").remove();

const margins = ({
top: 5,
right: 10,
bottom: 25,
left: 130
})

// Declare the x (horizontal position) scale.
const x = d3.scaleLinear(d3.extent(dataConvLSTM_50, d => d.time), [margins.left, width - margins.right])

// Declare the y (vertical position) scale.
const y = d3.scaleLinear()
.domain([0.075, 0.13]).nice()
.range([chartHeight - margins.bottom, margins.top])

const lineTrain = d3.line()
.x(d => x(d.time))
.y(d => y(d.train))

const lineVal = d3.line()
.x(d => x(d.time))
.y(d => y(d.val))

svg.append("g")
.attr("transform", `translate(0,${chartHeight - margins.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80))
.call(g => g.select(".domain").remove())
.call((g) => g.append("text")
.attr("x", width - margins.right)
.attr("y", margins.bottom - 10)
.attr("fill", "currentColor")
.attr("text-anchor", "end")
.attr("font-weight", "bold")
.text("epoch"))

svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line")
.clone()
.attr("x2", width - margins.right - margins.left)
.attr("stroke-opacity", d => d === 0 ? 1 : 0.1))
.call(g => g.append("text")
.attr("fill", "#000")
.attr("x", 5)
.attr("y", margins.top)
.attr("dy", "0.32em")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("mean absolute error"))

// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "teal")
.attr("stroke-width", 2)
.attr("d", lineTrain(dataConvLSTM_50))

// Append a path for the line.
svg.append("path")
.attr("fill", "none")
.attr("stroke", "#E4572E")
.attr("stroke-width", 2)
.attr("d", lineVal(dataConvLSTM_50))

// Add a title
const title = svg.append("g")
.append("text")
.attr("x", margins.left / 3)
.attr("y", (chartHeight / 2))
.attr("text-anchor", "middle")
.attr("front-weight", "bold")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-size", "15px")
.text("ConvLSTM")
return svg.node();
}
Insert cell
import {textcolor} from "@observablehq/text-color-annotations-in-markdown"
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