StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics
https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
8.2 KiB
304 lines
8.2 KiB
4 years ago
|
'use strict'
|
||
|
|
||
|
const t = require('tape')
|
||
|
const cluster = require('./')
|
||
|
const approxEqual = require('almost-equal')
|
||
|
|
||
|
t('quad: offsets case', t => {
|
||
|
let points = [.15,.8, .2,.15, .6,.6, .6,.45, .8,.1, .9,.6, .91,.61]
|
||
|
let index = cluster(points, {bounds: [0,0,1,1]})
|
||
|
|
||
|
t.deepEqual(index.slice(), [0, 1,3,2, 4,5, 6])
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t('quad: output container', t => {
|
||
|
let points = [.15,.8, .2,.15, .6,.6, .6,.45, .8,.1, .9,.6, .91,.61]
|
||
|
let arr = []
|
||
|
let index = cluster(points, {bounds: [0,0,1,1], output: arr})
|
||
|
|
||
|
t.deepEqual(arr.slice(), [0, 1,3,2, 4,5, 6])
|
||
|
t.equal(index, arr)
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
|
||
|
t('quad: max depth', t => {
|
||
|
let points = []
|
||
|
|
||
|
for (let i = 0; i < 1e4; i++) {
|
||
|
points.push(0, 0)
|
||
|
}
|
||
|
|
||
|
let index = cluster(points)
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t('quad: lod method', t => {
|
||
|
let points = [0,0, 1,1, 2,2, 3,3, 4,4, 5,5, 6,6, 7,7]
|
||
|
|
||
|
let index = cluster(points)
|
||
|
|
||
|
t.deepEqual(index.range({lod: true, d: 1e-10}), [[0,1], [1,3], [3,6], [6,8]])
|
||
|
t.deepEqual(index.range({lod: true, level: 400}), [[0,1], [1,3], [3,6], [6,8]])
|
||
|
t.deepEqual(index.range({lod: true}), [[0,1], [1,3], [3,6], [6,8]])
|
||
|
t.deepEqual(index.range([1,6], {lod: true}), [[0,1], [1,2], [3,4], [6,7]])
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t('quad: selection', t => {
|
||
|
let points = [0,0, 1,1, 2,2, 3,3, 4,4, 5,5, 6,6, 7,7]
|
||
|
|
||
|
let index = cluster(points)
|
||
|
|
||
|
t.deepEqual(index.range(), [0, 1, 2, 3, 4, 5, 6, 7])
|
||
|
t.deepEqual(index.range(1,1,6,6), [1, 2, 3, 4, 5, 6])
|
||
|
t.deepEqual(index.range(2,1,5,6), [2, 3, 4, 5])
|
||
|
t.deepEqual(index.range(1,2,6,5), [2, 3, 4, 5])
|
||
|
t.deepEqual(index.range(1,3,5,6), [3, 4, 5])
|
||
|
|
||
|
t.deepEqual(index.range(5,6,1,3), [3, 4, 5])
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t.skip('quad: snap-points-2d cases', t => {
|
||
|
function verifySnap(srcPoints) {
|
||
|
let numPoints = srcPoints.length>>>1
|
||
|
|
||
|
let {levels, ids, weights, points, bounds} = cluster(srcPoints)
|
||
|
let npoints = points
|
||
|
|
||
|
let sx = bounds[0]
|
||
|
let sy = bounds[1]
|
||
|
let sw = bounds[2] - bounds[0]
|
||
|
let sh = bounds[3] - bounds[1]
|
||
|
|
||
|
for(let i=0; i < numPoints; ++i) {
|
||
|
let id = ids[i]
|
||
|
t.ok(approxEqual(sx + sw*npoints[2*i], srcPoints[2*id], approxEqual.FLT_EPSILON),
|
||
|
'id perm ok: ' + id + ' ' + srcPoints[2*id] + ' = ' + (sx + sw*npoints[2*i]))
|
||
|
t.ok(approxEqual(sy + sh*npoints[2*i+1], srcPoints[2*id+1], approxEqual.FLT_EPSILON), 'id perm ok: ' + id + ' ' + srcPoints[2*id+1] + ' = ' + (sy + sh*npoints[2*i+1]))
|
||
|
}
|
||
|
|
||
|
t.equals(levels[levels.length-1].offset, 0, 'last item')
|
||
|
t.equals(levels[0].offset+levels[0].count, numPoints, 'first item')
|
||
|
|
||
|
for(let i=0; i < levels.length; ++i) {
|
||
|
let s = levels[i]
|
||
|
let r = s.pixelSize
|
||
|
let offs = s.offset
|
||
|
let count = s.count
|
||
|
|
||
|
if(i > 0) {
|
||
|
t.equals(offs+count, levels[i-1].offset, 'offset for ' + i)
|
||
|
t.ok(r < levels[i-1].pixelSize, 'test scales ok')
|
||
|
}
|
||
|
k_loop:
|
||
|
for(let k=offs-1; k >= 0; --k) {
|
||
|
let ax = npoints[2*k]
|
||
|
let ay = npoints[2*k+1]
|
||
|
|
||
|
let mind = Infinity
|
||
|
|
||
|
for(let j=offs; j < offs+count; ++j) {
|
||
|
let x = npoints[2*j]
|
||
|
let y = npoints[2*j+1]
|
||
|
|
||
|
mind = Math.min(mind, Math.max(Math.abs(ax-x), Math.abs(ay-y)))
|
||
|
}
|
||
|
|
||
|
t.ok(mind <= 2.0 * r, k + ':' + ax + ',' + ay + ' is not covered - closest pt = ' + mind)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
verifySnap([
|
||
|
1, 1,
|
||
|
2, 2,
|
||
|
3, 3,
|
||
|
4, 4,
|
||
|
5, 5
|
||
|
])
|
||
|
|
||
|
verifySnap([
|
||
|
0,0,
|
||
|
0,0,
|
||
|
0,0,
|
||
|
0,0
|
||
|
])
|
||
|
|
||
|
verifySnap([
|
||
|
1, 2,
|
||
|
2, 5,
|
||
|
3, 6,
|
||
|
4, -1
|
||
|
])
|
||
|
|
||
|
let pts = new Array(100)
|
||
|
for(let i=0; i < 100; ++i) {
|
||
|
pts[i] = Math.random()
|
||
|
}
|
||
|
verifySnap(pts)
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t.skip('quad: linear case', t => {
|
||
|
let {levels, points, ids, weights} = cluster([1,1,2,2,3,3,4,4,5,5])
|
||
|
|
||
|
t.deepEqual(ids, [2, 4, 1, 3, 0])
|
||
|
t.deepEqual(points, [ 0.5, 0.5, 1, 1, 0.25, 0.25, 0.75, 0.75, 0, 0 ])
|
||
|
t.deepEqual(weights, [1, 1, 2, 2, 5])
|
||
|
t.deepEqual(levels, [
|
||
|
{ count: 1, offset: 4, pixelSize: 2 },
|
||
|
{ count: 2, offset: 2, pixelSize: 1 },
|
||
|
{ count: 2, offset: 0, pixelSize: 0.5 }
|
||
|
])
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t.skip('quad: no arguments', t => {
|
||
|
let levels = cluster([0,0, 1,1, 2,2])
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t.skip('quad: larger bounds', t => {
|
||
|
let pos = [0,0, 1,1, 2,2, 3,3, 4,4]
|
||
|
|
||
|
let {levels} = cluster(pos.slice(), { bounds: [0,0,4,4] })
|
||
|
t.deepEqual(levels, [
|
||
|
{pixelSize: 2, offset: 4, count: 1},
|
||
|
{pixelSize: 1, offset: 2, count: 2},
|
||
|
{pixelSize: 0.5, offset: 0, count: 2}
|
||
|
])
|
||
|
|
||
|
let index = cluster(pos.slice(), { bounds: [0,0,40,40] })
|
||
|
levels = index.levels
|
||
|
|
||
|
t.deepEqual(levels, [
|
||
|
{pixelSize: 20, offset: 4, count: 1},
|
||
|
{pixelSize: 10, offset: 3, count: 1},
|
||
|
{pixelSize: 5, offset: 2, count: 1},
|
||
|
{pixelSize: 2.5, offset: 1, count: 1},
|
||
|
{pixelSize: 1.25, offset: 0, count: 1}
|
||
|
])
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t.skip('quad: group id', t => {
|
||
|
// TODO
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t.skip('kd: creates an index', t => {
|
||
|
var points = [
|
||
|
54,1, 97,21, 65,35, 33,54, 95,39, 54,3, 53,54, 84,72, 33,34, 43,15, 52,83, 81,23, 1,61, 38,74,
|
||
|
11,91, 24,56, 90,31, 25,57, 46,61, 29,69, 49,60, 4,98, 71,15, 60,25, 38,84, 52,38, 94,51, 13,25,
|
||
|
77,73, 88,87, 6,27, 58,22, 53,28, 27,91, 96,98, 93,14, 22,93, 45,94, 18,28, 35,15, 19,81, 20,81,
|
||
|
67,53, 43,3, 47,66, 48,34, 46,12, 32,38, 43,12, 39,94, 88,62, 66,14, 84,30, 72,81, 41,92, 26,4,
|
||
|
6,76, 47,21, 57,70, 71,82, 50,68, 96,18, 40,31, 78,53, 71,90, 32,14, 55,6, 32,88, 62,32, 21,67,
|
||
|
73,81, 44,64, 29,50, 70,5, 6,22, 68,3, 11,23, 20,42, 21,73, 63,86, 9,40, 99,2, 99,76, 56,77,
|
||
|
83,6, 21,72, 78,30, 75,53, 41,11, 95,20, 30,38, 96,82, 65,48, 33,18, 87,28, 10,10, 40,34,
|
||
|
10,20, 47,29, 46,78];
|
||
|
|
||
|
var ids = [
|
||
|
97,74,95,30,77,38,76,27,80,55,72,90,88,48,43,46,65,39,62,93,9,96,47,8,3,12,15,14,21,41,36,40,69,56,85,78,17,71,44,
|
||
|
19,18,13,99,24,67,33,37,49,54,57,98,45,23,31,66,68,0,32,5,51,75,73,84,35,81,22,61,89,1,11,86,52,94,16,2,6,25,92,
|
||
|
42,20,60,58,83,79,64,10,59,53,26,87,4,63,50,7,28,82,70,29,34,91];
|
||
|
|
||
|
var coords = [
|
||
|
10,20,6,22,10,10,6,27,20,42,18,28,11,23,13,25,9,40,26,4,29,50,30,38,41,11,43,12,43,3,46,12,32,14,35,15,40,31,33,18,
|
||
|
43,15,40,34,32,38,33,34,33,54,1,61,24,56,11,91,4,98,20,81,22,93,19,81,21,67,6,76,21,72,21,73,25,57,44,64,47,66,29,
|
||
|
69,46,61,38,74,46,78,38,84,32,88,27,91,45,94,39,94,41,92,47,21,47,29,48,34,60,25,58,22,55,6,62,32,54,1,53,28,54,3,
|
||
|
66,14,68,3,70,5,83,6,93,14,99,2,71,15,96,18,95,20,97,21,81,23,78,30,84,30,87,28,90,31,65,35,53,54,52,38,65,48,67,
|
||
|
53,49,60,50,68,57,70,56,77,63,86,71,90,52,83,71,82,72,81,94,51,75,53,95,39,78,53,88,62,84,72,77,73,99,76,73,81,88,
|
||
|
87,96,98,96,82];
|
||
|
var index = cluster(points, { nodeSize:10, type: 'kd', sort: false });
|
||
|
|
||
|
t.same(index.ids, ids, 'ids are kd-sorted');
|
||
|
// t.same(index.points, points, 'coords are kd-sorted');
|
||
|
|
||
|
t.end();
|
||
|
})
|
||
|
|
||
|
t.skip('kd: range search', t => {
|
||
|
var pts = points
|
||
|
|
||
|
var index = cluster(pts, {nodeSize:10, type: 'kd'});
|
||
|
|
||
|
var result = index.range(20, 30, 50, 70);
|
||
|
|
||
|
t.same(result, [60,20,45,3,17,71,44,19,18,15,69,90,62,96,47,8,77,72], 'returns ids');
|
||
|
|
||
|
for (var i = 0; i < result.length; i++) {
|
||
|
var p = pts[result[i]];
|
||
|
if (p[0] < 20 || p[0] > 50 || p[1] < 30 || p[1] > 70)
|
||
|
t.fail('result point in range');
|
||
|
}
|
||
|
t.pass('result points in range');
|
||
|
|
||
|
for (i = 0; i < ids.length; i++) {
|
||
|
p = pts[ids[i]];
|
||
|
if (result.indexOf(ids[i]) < 0 && p[0] >= 20 && p[0] <= 50 && p[1] >= 30 && p[1] <= 70)
|
||
|
t.fail('outside point not in range');
|
||
|
}
|
||
|
t.pass('outside points not in range');
|
||
|
|
||
|
t.end();
|
||
|
})
|
||
|
|
||
|
t.skip('kd: radius search', t => {
|
||
|
var pts = points
|
||
|
|
||
|
var index = cluster(pts, {nodeSize:10, type: 'kd'});
|
||
|
|
||
|
var qp = [50, 50];
|
||
|
var r = 20;
|
||
|
var r2 = 20 * 20;
|
||
|
|
||
|
var result = index.within(qp[0], qp[1], r);
|
||
|
|
||
|
t.same(result, [60,6,25,92,42,20,45,3,71,44,18,96], 'returns ids');
|
||
|
|
||
|
for (var i = 0; i < result.length; i++) {
|
||
|
var p = pts[result[i]];
|
||
|
if (sqDist(p, qp) > r2) t.fail('result point in range');
|
||
|
}
|
||
|
t.pass('result points in range');
|
||
|
|
||
|
for (i = 0; i < ids.length; i++) {
|
||
|
p = pts[ids[i]];
|
||
|
if (result.indexOf(ids[i]) < 0 && sqDist(p, qp) <= r2)
|
||
|
t.fail('outside point not in range');
|
||
|
}
|
||
|
t.pass('outside points not in range');
|
||
|
|
||
|
t.end();
|
||
|
})
|
||
|
|
||
|
t('performance', t => {
|
||
|
let N = 1e6
|
||
|
let points = new Float64Array(N)
|
||
|
let ids = new Uint32Array(N)
|
||
|
|
||
|
for (let i = 0; i < N; i++) {
|
||
|
points[i] = Math.random()
|
||
|
ids[i] = i
|
||
|
}
|
||
|
|
||
|
console.time(1)
|
||
|
cluster(points, {sort: false})
|
||
|
console.timeEnd(1)
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|