@ai-on-browser/data-analysis-models
Version:
Data analysis model package without any dependencies
144 lines (131 loc) • 3.44 kB
JavaScript
import Matrix from '../util/matrix.js'
/**
* Autoregressive model
*/
export default class AR {
// https://ja.wikipedia.org/wiki/%E8%87%AA%E5%B7%B1%E5%9B%9E%E5%B8%B0%E7%A7%BB%E5%8B%95%E5%B9%B3%E5%9D%87%E3%83%A2%E3%83%87%E3%83%AB
// https://qiita.com/yutera12/items/8502f0530f907354b56a
// http://www.mi.u-tokyo.ac.jp/mds-oudan/lecture_document_2019_math7/%E6%99%82%E7%B3%BB%E5%88%97%E8%A7%A3%E6%9E%90%EF%BC%88%EF%BC%96%EF%BC%89_2019.pdf
/**
* @param {number} p Order
* @param {'lsm' | 'yuleWalker' | 'levinson' | 'householder'} [method] Method name
*/
constructor(p, method = 'lsm') {
this._p = p
this._method = method
}
/**
* Fit model.
* @param {number[]} data Training data
*/
fit(data) {
if (this._method === 'lsm') {
this._lsm(data)
} else if (this._method === 'yuleWalker') {
this._yuleWalker(data)
} else if (this._method === 'levinson') {
this._levinson(data)
} else if (this._method === 'householder') {
this._householder(data)
}
}
_lsm(x) {
const n = x.length
const g = new Matrix(n - this._p, 1, x.slice(this._p))
const G = new Matrix(n - this._p, this._p)
for (let i = 0; i < n - this._p; i++) {
for (let j = 0; j < this._p; j++) {
G.set(i, j, x[i + this._p - 1 - j])
}
}
const Gx = G.tDot(G)
this._phi = Gx.solve(G.t).dot(g)
}
_yuleWalker(x) {
const n = x.length
const g = new Matrix(this._p, 1)
const G = new Matrix(this._p, this._p)
const mean = x.reduce((s, v) => s + v, 0) / n
for (let i = 0; i <= this._p; i++) {
let s = 0
for (let k = 0; k < n - i; k++) {
s += (x[k] - mean) * (x[k + i] - mean)
}
s /= n - i
if (i > 0) {
g.set(i - 1, 0, s)
}
if (i < this._p) {
for (let k = 0; k < this._p - i; k++) {
G.set(k, i + k, s)
G.set(i + k, k, s)
}
}
}
this._phi = G.solve(g)
this._variance = G.at(0, 0) - this._phi.tDot(g).toScaler()
}
_levinson(x) {
const n = x.length
const c = []
const mean = x.reduce((s, v) => s + v, 0) / n
for (let i = 0; i <= this._p; i++) {
let s = 0
for (let k = 0; k < n - i; k++) {
s += (x[k] - mean) * (x[k + i] - mean)
}
s /= n - i
c[i] = s
}
const v = []
v[0] = c[0]
let a = []
for (let m = 0; m < this._p; m++) {
const na = []
na[m] = c[m + 1]
for (let j = 0; j < m; j++) {
na[m] -= a[j] * c[m - j]
}
na[m] /= v[m]
for (let j = 0; j < m; j++) {
na[j] = a[j] - na[m] * a[m - j - 1]
}
v[m + 1] = v[m] * (1 - na[m] ** 2)
a = na
}
this._phi = Matrix.fromArray(a)
}
_householder(x) {
const n = x.length
const Z = new Matrix(n - this._p, this._p + 1)
for (let i = 0; i < n - this._p; i++) {
for (let j = 0; j < this._p; j++) {
Z.set(i, j, x[i + this._p - 1 - j])
}
Z.set(i, this._p, x[i + this._p])
}
const [, s] = Z.qr()
const sx = s.block(0, 0, this._p, this._p)
const sy = s.block(0, this._p, this._p, this._p + 1)
this._phi = sx.solveUpperTriangular(sy)
}
/**
* Returns predicted future values.
* @param {number[]} data Sample data
* @param {number} k Prediction count
* @returns {number[]} Predicted values
*/
predict(data, k) {
const preds = []
const lasts = data.slice(data.length - this._p)
lasts.reverse()
for (let i = 0; i < k; i++) {
const last = new Matrix(1, this._p, lasts)
const pred = last.dot(this._phi).toScaler()
preds.push(pred)
lasts.unshift(pred)
lasts.pop()
}
return preds
}
}