From 7c2c5606496efc21850cd2bc143887482db170e5 Mon Sep 17 00:00:00 2001 From: Ishii Norimi <47292902+ishii-norimi@users.noreply.github.com> Date: Sat, 18 Nov 2023 19:44:52 +0900 Subject: [PATCH] Move h parameter of KDE from fit method to constructor (#692) * Move h parameter of KDE from fit method to constructor * Fix JSDoc --- lib/model/kernel_density_estimator.js | 11 ++++---- .../model/kernel_density_estimator.test.js | 25 ++++++++++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/lib/model/kernel_density_estimator.js b/lib/model/kernel_density_estimator.js index 564ba923..07a6fda5 100644 --- a/lib/model/kernel_density_estimator.js +++ b/lib/model/kernel_density_estimator.js @@ -5,10 +5,11 @@ export default class KernelDensityEstimator { // https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E5%AF%86%E5%BA%A6%E6%8E%A8%E5%AE%9A // http://ibis.t.u-tokyo.ac.jp/suzuki/lecture/2015/dataanalysis/L9.pdf /** + * @param {number} [h=0] Smoothing parameter for the kernel * @param {'gaussian' | 'rectangular' | 'triangular' | 'epanechnikov' | 'biweight' | 'triweight' | function (number): number} [kernel=gaussian] Kernel name */ - constructor(kernel = 'gaussian') { - this._h = 0 + constructor(h = 0, kernel = 'gaussian') { + this._h = h if (typeof kernel === 'function') { this._kernel = kernel } else { @@ -39,13 +40,11 @@ export default class KernelDensityEstimator { * Fit model. * * @param {Array>} x Training data - * @param {number} h Smoothing parameter for the kernel */ - fit(x, h = 0) { + fit(x) { this._x = x - if (h > 0) { - this._h = h + if (this._h > 0) { return } diff --git a/tests/lib/model/kernel_density_estimator.test.js b/tests/lib/model/kernel_density_estimator.test.js index 7aaf6d35..1c2cc9fe 100644 --- a/tests/lib/model/kernel_density_estimator.test.js +++ b/tests/lib/model/kernel_density_estimator.test.js @@ -7,8 +7,27 @@ import KernelDensityEstimator from '../../../lib/model/kernel_density_estimator. import { correlation } from '../../../lib/evaluate/regression.js' describe('density estimation', () => { + test('default', () => { + const model = new KernelDensityEstimator() + const n = 500 + const x = Matrix.concat(Matrix.randn(n, 2, 0, 0.1), Matrix.randn(n, 2, 5, 0.1)).toArray() + + model.fit(x) + const y = model.predict(x) + expect(y).toHaveLength(x.length) + + const p = [] + for (let i = 0; i < x.length; i++) { + const p1 = Math.exp(-x[i].reduce((s, v) => s + v ** 2, 0) / (2 * 0.1)) / (2 * Math.PI * 0.1) + const p2 = Math.exp(-x[i].reduce((s, v) => s + (v - 5) ** 2, 0) / (2 * 0.1)) / (2 * Math.PI * 0.1) + p[i] = (p1 + p2) / 2 + } + const corr = correlation(y, p) + expect(corr).toBeGreaterThan(0.9) + }) + test.each([undefined, 'gaussian', 'triangular', 'epanechnikov', 'biweight', 'triweight'])('kernel %s', kernel => { - const model = new KernelDensityEstimator(kernel) + const model = new KernelDensityEstimator(0, kernel) const n = 500 const x = Matrix.concat(Matrix.randn(n, 2, 0, 0.1), Matrix.randn(n, 2, 5, 0.1)).toArray() @@ -27,7 +46,7 @@ describe('density estimation', () => { }) test.each(['rectangular'])('kernel %s', kernel => { - const model = new KernelDensityEstimator(kernel) + const model = new KernelDensityEstimator(0, kernel) const n = 500 const x = Matrix.concat(Matrix.randn(n, 2, 0, 0.1), Matrix.randn(n, 2, 5, 0.1)).toArray() @@ -46,7 +65,7 @@ describe('density estimation', () => { }) test('custom kernel', () => { - const model = new KernelDensityEstimator(v => 1 / (v + 1.0e-8)) + const model = new KernelDensityEstimator(1.0, v => 1 / (v + 1.0e-8)) const n = 500 const x = Matrix.concat(Matrix.randn(n, 2, 0, 0.1), Matrix.randn(n, 2, 5, 0.1)).toArray()