UNPKG

commons-math-interpolation

Version:

A partial port of the Apache Commons Math Interpolation package, including Akima cubic spline interpolation and LOESS/LOWESS local regression.

81 lines 3.67 kB
import { checkStrictlyIncreasing, trimPoly, evaluatePolySegment } from "./Utils.js"; const EPSILON = Number.EPSILON; export function createAkimaSplineInterpolator(xVals, yVals) { const segmentCoeffs = computeAkimaPolyCoefficients(xVals, yVals); const xValsCopy = Float64Array.from(xVals); return (x) => evaluatePolySegment(xValsCopy, segmentCoeffs, x); } export function computeAkimaPolyCoefficients(xVals, yVals) { if (xVals.length != yVals.length) { throw new Error("Dimension mismatch for xVals and yVals."); } if (xVals.length < 5) { throw new Error("Number of points is too small."); } checkStrictlyIncreasing(xVals); const n = xVals.length - 1; const differences = new Float64Array(n); const weights = new Float64Array(n); for (let i = 0; i < n; i++) { differences[i] = (yVals[i + 1] - yVals[i]) / (xVals[i + 1] - xVals[i]); } for (let i = 1; i < n; i++) { weights[i] = Math.abs(differences[i] - differences[i - 1]); } const firstDerivatives = new Float64Array(n + 1); for (let i = 2; i < n - 1; i++) { const wP = weights[i + 1]; const wM = weights[i - 1]; if (Math.abs(wP) < EPSILON && Math.abs(wM) < EPSILON) { const xv = xVals[i]; const xvP = xVals[i + 1]; const xvM = xVals[i - 1]; firstDerivatives[i] = (((xvP - xv) * differences[i - 1]) + ((xv - xvM) * differences[i])) / (xvP - xvM); } else { firstDerivatives[i] = ((wP * differences[i - 1]) + (wM * differences[i])) / (wP + wM); } } firstDerivatives[0] = differentiateThreePoint(xVals, yVals, 0, 0, 1, 2); firstDerivatives[1] = differentiateThreePoint(xVals, yVals, 1, 0, 1, 2); firstDerivatives[n - 1] = differentiateThreePoint(xVals, yVals, n - 1, n - 2, n - 1, n); firstDerivatives[n] = differentiateThreePoint(xVals, yVals, n, n - 2, n - 1, n); return computeHermitePolyCoefficients(xVals, yVals, firstDerivatives); } function differentiateThreePoint(xVals, yVals, indexOfDifferentiation, indexOfFirstSample, indexOfSecondsample, indexOfThirdSample) { const x0 = yVals[indexOfFirstSample]; const x1 = yVals[indexOfSecondsample]; const x2 = yVals[indexOfThirdSample]; const t = xVals[indexOfDifferentiation] - xVals[indexOfFirstSample]; const t1 = xVals[indexOfSecondsample] - xVals[indexOfFirstSample]; const t2 = xVals[indexOfThirdSample] - xVals[indexOfFirstSample]; const a = (x2 - x0 - (t2 / t1 * (x1 - x0))) / (t2 * t2 - t1 * t2); const b = (x1 - x0 - a * t1 * t1) / t1; return (2 * a * t) + b; } function computeHermitePolyCoefficients(xVals, yVals, firstDerivatives) { if (xVals.length != yVals.length || xVals.length != firstDerivatives.length) { throw new Error("Dimension mismatch"); } if (xVals.length < 2) { throw new Error("Not enough points."); } const n = xVals.length - 1; const segmentCoeffs = new Array(n); for (let i = 0; i < n; i++) { const w = xVals[i + 1] - xVals[i]; const w2 = w * w; const yv = yVals[i]; const yvP = yVals[i + 1]; const fd = firstDerivatives[i]; const fdP = firstDerivatives[i + 1]; const coeffs = new Float64Array(4); coeffs[0] = yv; coeffs[1] = firstDerivatives[i]; coeffs[2] = (3 * (yvP - yv) / w - 2 * fd - fdP) / w; coeffs[3] = (2 * (yv - yvP) / w + fd + fdP) / w2; segmentCoeffs[i] = trimPoly(coeffs); } return segmentCoeffs; } //# sourceMappingURL=Akima.js.map