Public
Edited
Sep 14, 2024
Paused
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
x = [32,37,42,47,52,57,62,67,72,77,82,87,92]
Insert cell
y = [749,1525,1947,2201,2380,2537,2671,2758,2803,2943,3007,2979,2992]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
model = function(x,p){return x.map(function(x_i){return (
p[0]*Math.exp(-0.5*(Math.pow((x_i-p[1])/p[2],2))) + p[3]
)})}
Insert cell
Insert cell
p_init = [-60000,-90,40,3100];
Insert cell
Insert cell
// Define bounds for the parameters
Opt = {
return {
maxIter: 500, // Optional
bounds: [
[-70000, -50000], // Bounds for p[0] => [-Infinity, Infinity], // No bounds
[-90, 100], // Bounds for p[1]
[1, 40], // Bounds for p[2]
[3000, 3100] // Bounds for p[3]
],
}
};
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
Insert cell
// Modified fminsearch function with bounds: Please note that this implementation is not optimized.
// Adapted from: https://github.com/jonasalmeida/fminsearch
// See associated notebook: https://observablehq.com/@christophe-yamahata/nonlinear-regression-with-bounds-in-javascript
fminsearch = function(fun, Parm0, x, y, Opt) {
if (!Opt) { Opt = {} };
if (!Opt.maxIter) { Opt.maxIter = 1000 };
if (!Opt.step) { // initial step is 1/100 of initial value (remember not to use zero in Parm0)
Opt.step = Parm0.map(function(p) { return p / 100 });
Opt.step = Opt.step.map(function(si) { if (si == 0) { return 1 } else { return si } }); // convert null steps into 1's
};
if (typeof (Opt.display) == 'undefined') { Opt.display = true };
if (!Opt.objFun) { Opt.objFun = function(y, yp) { return y.map(function(yi, i) { return Math.pow((yi - yp[i]), 2) }).reduce(function(a, b) { return a + b }) } } //SSD
if (!Opt.bounds) { Opt.bounds = Parm0.map(function() { return [-Infinity, Infinity] }) }; // Default no bounds

var cloneVector = function(V) { return V.map(function(v) { return v }) };
var ya, y0, yb, fP0, fP1;
var P0 = cloneVector(Parm0), P1 = cloneVector(Parm0);
var n = P0.length;
// var step = Opt.step;
let step = cloneVector(Opt.step); // Ensure step is reinitialized
var funParm = function(P) { return Opt.objFun(y, fun(x, P)) } //function (of Parameters) to minimize

// Apply bounds
var applyBounds = function(P, bounds) {
return P.map(function(p, i) {
return Math.max(bounds[i][0], Math.min(bounds[i][1], p));
});
}

// Silly multi-univariate screening
for (var i = 0; i < Opt.maxIter; i++) {
for (var j = 0; j < n; j++) { // take a step for each parameter
P1 = cloneVector(P0);
P1[j] += step[j];
P1 = applyBounds(P1, Opt.bounds); // Apply bounds after updating the parameter

if (funParm(P1) < funParm(P0)) { // if parm value going in the right direction
step[j] = 1.2 * step[j]; // then go a little faster
P0 = cloneVector(P1);
}
else {
step[j] = -(0.5 * step[j]); // otherwise reverse and go slower
}
}
if (Opt.display) { if (i > (Opt.maxIter - 10)) { console.log(i + 1, funParm(P0), P0) } }
}
return P0
}
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
Insert cell
Insert cell
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