Skip to content

Commit d78b37b

Browse files
authored
Fix principal curve (#70)
1 parent 52fa59a commit d78b37b

File tree

5 files changed

+159
-5
lines changed

5 files changed

+159
-5
lines changed

js/view/principal_curve.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ var dispPC = function (elm, platform) {
2020
}
2121

2222
export default function (platform) {
23-
platform.setting.ml.draft = true
2423
platform.setting.ml.usage = 'Click and add data point. Next, click "Fit" button.'
2524
dispPC(platform.setting.ml.configElement, platform)
2625
}

lib/model/principal_curve.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import SmoothingSpline from './spline.js'
44

55
/**
66
* Principal curves
7-
*
8-
* @deprecated Does not work properly
97
*/
108
export default class PrincipalCurve {
119
// https://omedstu.jimdofree.com/2019/09/29/principal-curve-%E5%85%A5%E9%96%80/
@@ -39,10 +37,13 @@ export default class PrincipalCurve {
3937
const ln = 1000
4038

4139
for (let i = 0; i < 1; i++) {
40+
const uniql = l.copy()
41+
const lidx = uniql.unique()
42+
const xidx = x.row(lidx)
4243
const f = []
4344
for (let k = 0; k < m; k++) {
44-
const spl = new SmoothingSpline(0)
45-
spl.fit(l.value, x.col(k).value)
45+
const spl = new SmoothingSpline(0.1)
46+
spl.fit(uniql.value, xidx.col(k).value)
4647
f.push(spl)
4748
}
4849

lib/util/matrix.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,69 @@ export default class Matrix {
864864
return idx
865865
}
866866

867+
/**
868+
* Make it unique in the specified axis.
869+
*
870+
* @param {number} [axis=0]
871+
* @param {number} [tol=0]
872+
* @returns {number[]}
873+
*/
874+
unique(axis = 0, tol = 0) {
875+
const idx = []
876+
if (axis === 0) {
877+
let r = 0
878+
for (let i = 0; i < this.rows; i++) {
879+
let same_k = -1
880+
for (let k = 0; k < i; k++) {
881+
let issame = true
882+
for (let j = 0; issame && j < this.cols; j++) {
883+
if (Math.abs(this._value[i * this.cols + j] - this._value[k * this.cols + j]) > tol) {
884+
issame = false
885+
}
886+
}
887+
if (issame) {
888+
same_k = k
889+
break
890+
}
891+
}
892+
if (same_k < 0) {
893+
for (let j = 0; j < this.cols; j++) {
894+
this._value[r * this.cols + j] = this._value[i * this.cols + j]
895+
}
896+
idx.push(i)
897+
r++
898+
}
899+
}
900+
this._size[0] = r
901+
this._value.length = this.length
902+
} else if (axis === 1) {
903+
for (let j = 0; j < this.cols; j++) {
904+
let hasSame = false
905+
for (let k = 0; !hasSame && k < idx.length; k++) {
906+
hasSame = true
907+
for (let i = 0; hasSame && i < this.rows; i++) {
908+
if (Math.abs(this._value[i * this.cols + j] - this._value[i * this.cols + idx[k]]) > tol) {
909+
hasSame = false
910+
}
911+
}
912+
}
913+
if (!hasSame) {
914+
idx.push(j)
915+
}
916+
}
917+
for (let i = 0, p = 0; i < this.rows; i++) {
918+
for (let j = 0; j < idx.length; j++, p++) {
919+
this._value[p] = this._value[i * this.cols + idx[j]]
920+
}
921+
}
922+
this._size[1] = idx.length
923+
this._value.length = this.length
924+
} else {
925+
throw new MatrixException('Invalid axis.')
926+
}
927+
return idx
928+
}
929+
867930
/**
868931
* Returns resized matrix.
869932
*
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Matrix from '../../../lib/util/matrix.js'
2+
import PrincipalCurve from '../../../lib/model/principal_curve.js'
3+
4+
import { coRankingMatrix } from '../../../lib/evaluate/dimensionality_reduction.js'
5+
6+
test('dimensionality reduction', () => {
7+
const x = Matrix.randn(50, 5, 0, 0.2).concat(Matrix.randn(50, 5, 5, 0.2)).toArray()
8+
const model = new PrincipalCurve()
9+
10+
for (let i = 0; i < 10; i++) {
11+
model.fit(x)
12+
}
13+
const y = model.predict()
14+
const q = coRankingMatrix(x, y, 30, 20)
15+
expect(q).toBeGreaterThan(0.9)
16+
})

tests/lib/util/matrix.test.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,6 +1296,81 @@ describe('Matrix', () => {
12961296
})
12971297
})
12981298

1299+
describe('unique', () => {
1300+
test('axis 0', () => {
1301+
const org = Matrix.randn(10, 5)
1302+
org.set(1, 0, org.row(3))
1303+
const mat = org.copy()
1304+
1305+
const idx = mat.unique(0)
1306+
expect(mat.sizes).toEqual([9, 5])
1307+
expect(idx).toEqual([0, 1, 2, 4, 5, 6, 7, 8, 9])
1308+
for (let i = 0; i < mat.rows; i++) {
1309+
for (let j = 0; j < mat.cols; j++) {
1310+
expect(mat.at(i, j)).toBe(org.at(idx[i], j))
1311+
}
1312+
}
1313+
})
1314+
1315+
test('axis 0 tol', () => {
1316+
const org = Matrix.randn(10, 5)
1317+
org.set(
1318+
1,
1319+
0,
1320+
org.row(3).copyMap(v => v + (Math.random() * 2 - 1) * 1.0e-4)
1321+
)
1322+
const mat = org.copy()
1323+
1324+
const idx = mat.unique(0, 1.0e-4)
1325+
expect(mat.sizes).toEqual([9, 5])
1326+
expect(idx).toEqual([0, 1, 2, 4, 5, 6, 7, 8, 9])
1327+
for (let i = 0; i < mat.rows; i++) {
1328+
for (let j = 0; j < mat.cols; j++) {
1329+
expect(mat.at(i, j)).toBeCloseTo(org.at(idx[i], j))
1330+
}
1331+
}
1332+
})
1333+
1334+
test('axis 1', () => {
1335+
const org = Matrix.randn(5, 10)
1336+
org.set(0, 1, org.col(3))
1337+
const mat = org.copy()
1338+
1339+
const idx = mat.unique(1)
1340+
expect(mat.sizes).toEqual([5, 9])
1341+
expect(idx).toEqual([0, 1, 2, 4, 5, 6, 7, 8, 9])
1342+
for (let i = 0; i < mat.rows; i++) {
1343+
for (let j = 0; j < mat.cols; j++) {
1344+
expect(mat.at(i, j)).toBe(org.at(i, idx[j]))
1345+
}
1346+
}
1347+
})
1348+
1349+
test('axis 1 tol', () => {
1350+
const org = Matrix.randn(5, 10)
1351+
org.set(
1352+
0,
1353+
1,
1354+
org.col(3).copyMap(v => v + (Math.random() * 2 - 1) * 1.0e-4)
1355+
)
1356+
const mat = org.copy()
1357+
1358+
const idx = mat.unique(1, 1.0e-4)
1359+
expect(mat.sizes).toEqual([5, 9])
1360+
expect(idx).toEqual([0, 1, 2, 4, 5, 6, 7, 8, 9])
1361+
for (let i = 0; i < mat.rows; i++) {
1362+
for (let j = 0; j < mat.cols; j++) {
1363+
expect(mat.at(i, j)).toBeCloseTo(org.at(i, idx[j]))
1364+
}
1365+
}
1366+
})
1367+
1368+
test('fail invalid axis', () => {
1369+
const mat = Matrix.randn(5, 10)
1370+
expect(() => mat.unique(2)).toThrowError('Invalid axis.')
1371+
})
1372+
})
1373+
12991374
describe('resize', () => {
13001375
test.each([
13011376
[5, 6],

0 commit comments

Comments
 (0)