Public
Edited
Mar 12, 2023
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
/**
* Say something in the message area, the #rollingMessage,
*/
newMsg = (msg = "Empty message", type = "debug") => {
const node = document.createElement("p"),
parent = document.getElementById("rollingMsg"),
first = parent ? parent.getElementsByClassName("Msg")[0] : undefined;

node.innerHTML = `[${new Date().toLocaleString()}]\t[${type}]\t>>\t` + msg;
node.className = "Msg";
node.style = "";

if (first) {
parent.insertBefore(node, first);
} else {
if (parent) parent.appendChild(node);
}
}
Insert cell
// Style for the #rollingMessage
html`
<style type="text/css">

#rollingMsg {
max-height: 200px;
overflow-y: scroll;
background: darkslateblue;
color: burlywood;
}

.Msg {
animation-name: example;
animation-duration: 2s;
margin: 0px;
padding: 2px;
}

@keyframes example {
from {background-color: black;}
to {background-color: darkslateblue;}
}

</style>
`
Insert cell
height = (width * 9) / 16
Insert cell
// It will be added to the Gaussian Process solver, to obtain a valid solution
epsX = 1e-2
Insert cell
/**
* Refresh as evenScaleBtn is clicked
*/
{
evenScaleBtn;
evenScaleDataBuffer();
refresh();
}
Insert cell
/**
* Refresh as GaussianProcessParam.resetWidthPoints changes
*/
{
initDataBuffer(GaussianProcessParam.resetWithPoints);
refresh();
}
Insert cell
Insert cell
// computeGaussianProcess()
Insert cell
/**
* Provide the xi for the Gaussian process estimation,
* and the xi is the x values to be estimated,
* in the current version, the xi is ranged from 0 to 1, and points are 100
*/
gaussianProcessXi = {
const num = 100,
scaleX = d3.scaleLinear().domain([0, num]).range([0, 1]),
xi = [];

for (let i = 0; i < num; ++i) {
xi.push([scaleX(i)]);
}

return xi;
}
Insert cell
// Check the dataBuffer, if it shows nothing, recall it to check out the latest
dataBuffer
Insert cell
// Check the computeGaussianProcess, if it fails, recall it to check out the latest
computeGaussianProcess()
Insert cell
/**
* Call randomSelectOnMean() when randomSelect btn is clicked.
*/
{
randomSelect;
return randomSelectOnMean();
}
Insert cell
/**
* Randomly select the other (x, y) pairs along the estimated mean curve.
* the select is performed dataBuffer.length times to make sure the selection
* has the same numbers with the current dataBuffer.
* And the dataBuffer will be updated IN-PLACE
*/
randomSelectOnMean = () => {
const d = computeGaussianProcess().filter((d) => d.y < 1 && d.y > 0),
{ length } = dataBuffer;

d3.shuffle(d);

for (let i = 0; i < length; ++i) {
dataBuffer.shift();
}

d.slice(0, GaussianProcessParam.resetWithPoints).map(({ x, y }) => {
appendDataBuffer(x, y);
});

refresh();
return dataBuffer;
}
Insert cell
computeGaussianProcess()
Insert cell
/**
* Compute the Gaussian process solution based on dataBuffer.
* The solution's xi are gaussianProcessXi
*/
computeGaussianProcess = () => {
const eps = 1e-4,
xi = gaussianProcessXi;

const xf = dataBuffer.map((e) => [e.x]),
yf = dataBuffer.map((e) => [e.y]);

const kff = gaussianKernel(xf, xf),
kyy = gaussianKernel(xi, xi),
kfy = gaussianKernel(xf, xi);

console.log("----------------", kff, kyy, kfy);

const kffInv = math.inv(math.add(kff, eps));

console.log(kff, kyy, kfy, kffInv);

const mu = math.multiply(math.multiply(math.transpose(kfy), kffInv), yf),
cov = math.subtract(
kyy,
math.multiply(math.multiply(math.transpose(kfy), kffInv), kfy)
);
console.log(mu, cov);

const values = [];
for (let i = 0; i < xi.length; i++) {
const x = xi[i][0],
y = mu.subset(math.index(i, 0)),
d = cov.subset(math.index(i, i)) ** 0.5;
values.push({ x, y, d });
}
console.log(values);

return values;
}
Insert cell
/**
* Generate the gaussian kernal with the sample of x1 and x2
*/
gaussianKernel = (x1, x2) => {
const { l, sigma } = GaussianProcessParam;

const kernel = math.zeros(x1.length, x2.length);

const a = sigma ** 2;
const c = -1 / 2 / l ** 2;

for (let i = 0; i < x1.length; i++) {
for (let j = 0; j < x2.length; j++) {
kernel.subset(math.index(i, j), a * math.exp(c * norm2(x1[i], x2[j])));
}
}

return kernel;
}
Insert cell
/** Compute the norm2 between the x1 and x2 samples */
norm2 = (x1, x2) => {
let n = 0;
for (let i = 0; i < x1.length; i++) {
n += (x1[i] - x2[i]) ** 2;
}
return n;
}
Insert cell
Insert cell
// refresh()
Insert cell
myChart.data
Insert cell
/**
* Refresh the chartjs canvas,
* the sortDataBuffer() is called to sort the dataBuffer ^_^
* the computeGaussianProcess() is called to find the solve the GP problem
*/
refresh = () => {
sortDataBuffer();

const solution = computeGaussianProcess();

var data;

if (myChart.data.datasets.length > 3) {
myChart.data.datasets[0].data = dataBuffer.map((d) => {
return { x: d.x, y: d.y };
});

myChart.data.datasets[1].data = solution.map((d) => d.y);
myChart.data.datasets[2].data = solution.map(
(d) => d.y - d.d * GaussianProcessParam.stdScale
);
myChart.data.datasets[3].data = solution.map(
(d) => d.y + d.d * GaussianProcessParam.stdScale
);
} else {
data = {
labels: solution.map((d) => d.x),
datasets: [
// 0
{
label: "Samples",
data: dataBuffer.map((d) => {
return { x: d.x, y: d.y };
}),
fill: false,
borderColor: "red",
tension: 0.1,
animation: false
},
// 1
{
type: "line",
label: "Gaussian Process (mean)",
data: solution.map((d) => d.y),
borderColor: "rgb(75, 192, 192)",
tension: 0.1
},
// 2
{
type: "line",
label: "Gaussian Process (lower)",
data: solution.map((d) => d.y - d.d * GaussianProcessParam.stdScale),
borderColor: "gray",
tension: 0.1,
pointRadius: 1
},
// 3
{
type: "line",
label: "Gaussian Process (upper)",
data: solution.map((d) => d.y + d.d * GaussianProcessParam.stdScale),
borderColor: "gray",
tension: 0.1,
pointRadius: 1,
fill: "-1"
}
]
};

myChart.data = data;
}

myChart.options.plugins.title.text = `Now: ${new Date()}`;

myChart.update();

newMsg(`
The myChart is refreshed with ${dataBuffer.length} points
`);
}
Insert cell
/**
* Add the onClick handler for the chartjs
*/
{
myChart.options.onClick = (e) => {
if (!e.native.altKey) return;

const canvasPosition = Chart.helpers.getRelativePosition(e, myChart);

// Substitute the appropriate scale IDs
const dataX = myChart.scales.x.getValueForPixel(canvasPosition.x);
const dataY = myChart.scales.y.getValueForPixel(canvasPosition.y);
console.log({ e, canvasPosition, dataX, dataY });

appendDataBuffer(dataX, dataY);

refresh();
};

return newMsg(`
Added the onClick handler for myChart
`);
}
Insert cell
myChartBuffer = {
return { myChart: undefined };
}
Insert cell
/**
* Setup the chartjs
* !!! I can not solve the problem of
* myChart = Error: Canvas is already in use. Chart with ID '0' must be destroyed before the canvas with ID 'myChartJSCanvas' can be reused.
* so, if something is wrong, just refresh the page.
*/
myChart = {
const ctx = document.getElementById("myChartJSCanvas").getContext("2d");

if (myChartBuffer.myChart) {
// myChartBuffer.myChart.destroy();
// myChartBuffer.myChart = undefined;
}

const myChart = new Chart(ctx, {
type: "bubble",

options: {
responsive: true,
animations: {
tension: {
duration: 1000,
easing: "easeInOutElastic",
from: 1,
to: 0,
loop: false
}
},
scales: {
y: {
reverse: false,
min: -0.2,
max: 1.2
}
},
interaction: {
mode: "nearest",
axis: "x",
intersect: false
},
plugins: {
legend: {
position: "top"
},
title: {
display: true,
text: "Schedule"
}
},
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove"],
plugins: {
tooltip: {
// Tooltip will only receive click events
events: ["click"]
}
}
}
});

myChartBuffer.myChart = myChart;

return myChart;
}
Insert cell
Insert cell
sortedDataBuffer
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
sortedDataBuffer = sortDataBuffer()
Insert cell
initDataBuffer()
Insert cell
/**
* Reset the x values of the points in the dataBuffer,
* makesure they shares the same gaps between each two points.
*/
evenScaleDataBuffer = () => {
const n = dataBuffer.length,
scaleIdx = d3.scaleLinear().domain([0, n]).range([0, 1]);

sortDataBuffer();

dataBuffer.map((d, i) => {
d.x = scaleIdx(i);
});

newMsg(`
Even scale x-value in the dataBuffer.
`);
}
Insert cell
/**
* Sort the points in the dataBuffer by their x-values
*/
sortDataBuffer = () => {
dataBuffer.sort((a, b) => a.x - b.x);

newMsg(`
The dataBuffer is sorted
`);

return dataBuffer;
}
Insert cell
/**
* Append a new point to the dataBuffer,
* the function will settle the other issues for you.
* If not provided x and y, the point with random x, y value will be added,
* if provide x and not provide y, the point with given x and random y will be added.
*/
appendDataBuffer = (x, y) => {
var earlyReturnFlag = false;

if (x) {
dataBuffer.map((d) => {
if (Math.abs(d.x - x) < epsX) {
d.y = y ? y : dataBuffer.rndY();
d.i = dataBuffer.i;
dataBuffer.i += 1;
earlyReturnFlag = true;

newMsg(`
Changed (${x}, ${y}) in the dataBuffer.
`);
}
});
}

if (earlyReturnFlag) return;

dataBuffer.push({
x: x ? x : dataBuffer.rndX(),
y: y ? y : dataBuffer.rndY(),
i: dataBuffer.i
});
dataBuffer.i += 1;

newMsg(`
Appended (${x}, ${y}) to the dataBuffer.
`);
}
Insert cell
/**
* Make up a reasonable dataBuffer for initiate,
* if the num is not provided, I will use 7 points.
*/
initDataBuffer = (num) => {
const n = dataBuffer.length;
for (let i = 0; i < n; ++i) {
dataBuffer.pop();
}

const initNum = num ? num : 7;
for (let i = 0; i < initNum; ++i) {
appendDataBuffer();
}

evenScaleDataBuffer();

newMsg(`
The dataBuffer is initialized with ${dataBuffer.length} elements
`);
}
Insert cell
/**
* The dataBuffer is initialized as an empty array,
* the attr i refers the point's order,
* the attr rndX() refers how to compute a random x value for a reasonable point,
* the attr rndY() refers how to compute a y.
*/
dataBuffer = {
const data = [],
rndX = d3.randomUniform(0, 1),
rndY = d3.randomUniform(0, 1);

Object.assign(data, { i: 0, rndX, rndY });

return data;
}
Insert cell
Insert cell
pdfView(await FileAttachment("RW.pdf").url(), {
backgroundColor: "#776EA7",
fontColor: "#BDC6FF",
scale: 1.5
})
Insert cell
import { pdfView } from "@saneef/pdf-view"
Insert cell
Chart = require("https://cdn.jsdelivr.net/npm/chart.js")
Insert cell
d3 = require("d3")
Insert cell
import {toc} from "@bryangingechen/toc"
Insert cell
math = require("https://unpkg.com/mathjs@10.6.0/lib/browser/math.js")
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