/
spline_interpolation.js
75 lines (68 loc) · 1.82 KB
/
spline_interpolation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import Matrix from '../util/matrix.js'
/**
* Spline interpolation
*/
export default class SplineInterpolation {
// https://en.wikipedia.org/wiki/Spline_interpolation
constructor() {}
/**
* Fit model.
*
* @param {number[]} x Training data
* @param {number[]} y Target values
*/
fit(x, y) {
const n = x.length
const d = x.map((v, i) => [v, y[i]])
d.sort((a, b) => a[0] - b[0])
x = this._x = d.map(v => v[0])
y = this._y = d.map(v => v[1])
const A = Matrix.zeros(n, n)
const B = new Matrix(n, 1)
A.set(0, 0, 2 / (x[1] - x[0]))
B.set(0, 0, (3 * (y[1] - y[0])) / (x[1] - x[0]) ** 2)
for (let i = 1; i < n; i++) {
A.set(i - 1, i, 1 / (x[i] - x[i - 1]))
A.set(i, i - 1, 1 / (x[i] - x[i - 1]))
if (i < n - 1) {
A.set(i, i, 2 / (x[i] - x[i - 1]) + 2 / (x[i + 1] - x[i]))
B.set(
i,
0,
3 * ((y[i] - y[i - 1]) / (x[i] - x[i - 1]) ** 2 + (y[i + 1] - y[i]) / (x[i + 1] - x[i]) ** 2)
)
} else {
A.set(i, i, 2 / (x[i] - x[i - 1]))
B.set(i, 0, (3 * (y[i] - y[i - 1])) / (x[i] - x[i - 1]) ** 2)
}
}
const K = A.solve(B).value
this._a = []
this._b = []
for (let i = 0; i < n - 1; i++) {
this._a.push(K[i] * (x[i + 1] - x[i]) - (y[i + 1] - y[i]))
this._b.push(-K[i + 1] * (x[i + 1] - x[i]) + (y[i + 1] - y[i]))
}
}
/**
* Returns predicted interpolated values.
*
* @param {number[]} target Sample data
* @returns {number[]} Predicted values
*/
predict(target) {
const n = this._x.length
return target.map(v => {
if (v < this._x[0]) {
return this._y[0]
}
let i = 0
for (; i < n - 1 && this._x[i + 1] <= v; i++);
if (i === n - 1) {
return this._y[n - 1]
}
const t = (v - this._x[i]) / (this._x[i + 1] - this._x[i])
return (1 - t) * this._y[i] + t * this._y[i + 1] + t * (1 - t) * ((1 - t) * this._a[i] + t * this._b[i])
})
}
}