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.
451 lines
13 KiB
451 lines
13 KiB
(function (global, factory) {
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
(factory((global.d3 = global.d3 || {})));
|
|
}(this, (function (exports) { 'use strict';
|
|
|
|
function compareValue(compare) {
|
|
return function (a, b) {
|
|
return compare(a.outer.value, b.outer.value);
|
|
};
|
|
}
|
|
|
|
function constant(x) {
|
|
return function () {
|
|
return x;
|
|
};
|
|
}
|
|
|
|
/* Based on the d3v4 d3.chord() function by Mike Bostock
|
|
** Adjusted by Nadieh Bremer - July 2016 */
|
|
|
|
/* global d3 */
|
|
function loom() {
|
|
var tau = Math.PI * 2;
|
|
var padAngle = 0;
|
|
var sortGroups = null;
|
|
var sortSubgroups = null;
|
|
var sortLooms = null;
|
|
var emptyPerc = 0.2;
|
|
var heightInner = 20;
|
|
var widthInner = function widthInner() {
|
|
return 30;
|
|
};
|
|
var value = function value(d) {
|
|
return d.value;
|
|
};
|
|
var inner = function inner(d) {
|
|
return d.inner;
|
|
};
|
|
var outer = function outer(d) {
|
|
return d.outer;
|
|
};
|
|
|
|
function loomLayout(layoutData) {
|
|
// Nest the data on the outer variable
|
|
var data = d3.nest().key(outer).entries(layoutData);
|
|
|
|
var n = data.length;
|
|
|
|
// Loop over the outer groups and sum the values
|
|
|
|
var groupSums = [];
|
|
var groupIndex = d3.range(n);
|
|
var subgroupIndex = [];
|
|
var looms = [];
|
|
looms.groups = new Array(n);
|
|
var groups = looms.groups;
|
|
var numSubGroups = void 0;
|
|
looms.innergroups = [];
|
|
var uniqueInner = looms.innergroups;
|
|
var uniqueCheck = [];
|
|
var k = void 0;
|
|
var x = void 0;
|
|
var x0 = void 0;
|
|
var j = void 0;
|
|
var l = void 0;
|
|
var s = void 0;
|
|
var v = void 0;
|
|
var sum = void 0;
|
|
var section = void 0;
|
|
var remain = void 0;
|
|
var counter = void 0;
|
|
var reverseOrder = false;
|
|
var approxCenter = void 0;
|
|
k = 0;
|
|
numSubGroups = 0;
|
|
for (var i = 0; i < n; i += 1) {
|
|
v = data[i].values.length;
|
|
sum = 0;
|
|
for (j = 0; j < v; j += 1) {
|
|
sum += value(data[i].values[j]);
|
|
} // for j
|
|
groupSums.push(sum);
|
|
subgroupIndex.push(d3.range(v));
|
|
numSubGroups += v;
|
|
k += sum;
|
|
} // for i
|
|
|
|
// Sort the groups…
|
|
if (sortGroups) {
|
|
groupIndex.sort(function (a, b) {
|
|
return sortGroups(groupSums[a], groupSums[b]);
|
|
});
|
|
}
|
|
|
|
// Sort subgroups…
|
|
if (sortSubgroups) {
|
|
subgroupIndex.forEach(function (d, i) {
|
|
d.sort(function (a, b) {
|
|
return sortSubgroups(inner(data[i].values[a]), inner(data[i].values[b]));
|
|
});
|
|
});
|
|
}
|
|
|
|
// After which group are we past the center, taking into account the padding
|
|
// TODO: make something for if there is no "nice" split in two...
|
|
var padk = k * (padAngle / tau);
|
|
l = 0;
|
|
for (var _i = 0; _i < n; _i += 1) {
|
|
section = groupSums[groupIndex[_i]] + padk;
|
|
l += section;
|
|
if (l > (k + n * padk) / 2) {
|
|
// Check if the group should be added to left or right
|
|
remain = k + n * padk - (l - section);
|
|
approxCenter = remain / section < 0.5 ? groupIndex[_i] : groupIndex[_i - 1];
|
|
break;
|
|
} // if
|
|
} // for i
|
|
|
|
// How much should be added to k to make the empty part emptyPerc big of the total
|
|
var emptyk = k * emptyPerc / (1 - emptyPerc);
|
|
k += emptyk;
|
|
|
|
// Convert the sum to scaling factor for [0, 2pi].
|
|
k = Math.max(0, tau - padAngle * n) / k;
|
|
var dx = k ? padAngle : tau / n;
|
|
|
|
// Compute the start and end angle for each group and subgroup.
|
|
// Note: Opera has a bug reordering object literal properties!
|
|
var subgroups = new Array(numSubGroups);
|
|
x = emptyk * 0.25 * k; // starting with quarter of the empty part to the side;
|
|
counter = 0;
|
|
for (var _i2 = 0; _i2 < n; _i2 += 1) {
|
|
var di = groupIndex[_i2];
|
|
var outername = data[di].key;
|
|
|
|
x0 = x;
|
|
s = subgroupIndex[di].length;
|
|
for (j = 0; j < s; j += 1) {
|
|
var dj = reverseOrder ? subgroupIndex[di][s - 1 - j] : subgroupIndex[di][j];
|
|
|
|
v = value(data[di].values[dj]);
|
|
var innername = inner(data[di].values[dj]);
|
|
var a0 = x;
|
|
x += v * k;
|
|
var a1 = x;
|
|
subgroups[counter] = {
|
|
index: di,
|
|
subindex: dj,
|
|
startAngle: a0,
|
|
endAngle: a1,
|
|
value: v,
|
|
outername: outername,
|
|
innername: innername,
|
|
groupStartAngle: x0
|
|
};
|
|
|
|
// Check and save the unique inner names
|
|
if (!uniqueCheck[innername]) {
|
|
uniqueCheck[innername] = true;
|
|
uniqueInner.push({ name: innername });
|
|
} // if
|
|
|
|
counter += 1;
|
|
} // for j
|
|
groups[di] = {
|
|
index: di,
|
|
startAngle: x0,
|
|
endAngle: x,
|
|
value: groupSums[di],
|
|
outername: outername
|
|
};
|
|
x += dx;
|
|
// If this is the approximate center, add half of the empty piece for the bottom
|
|
if (approxCenter === di) x += emptyk * 0.5 * k;
|
|
// If you've crossed the bottom, reverse the order of the inner strings
|
|
if (x > Math.PI) reverseOrder = true;
|
|
} // for i
|
|
|
|
// Sort the inner groups in the same way as the strings
|
|
if (sortSubgroups) {
|
|
uniqueInner.sort(function (a, b) {
|
|
return sortSubgroups(a.name, b.name);
|
|
});
|
|
}
|
|
|
|
// Find x and y locations of the inner categories
|
|
var m = uniqueInner.length;
|
|
for (var _i3 = 0; _i3 < m; _i3 += 1) {
|
|
uniqueInner[_i3].x = 0;
|
|
uniqueInner[_i3].y = -m * heightInner / 2 + _i3 * heightInner;
|
|
uniqueInner[_i3].offset = widthInner(uniqueInner[_i3].name, _i3);
|
|
} // for i
|
|
|
|
// Generate bands for each (non-empty) subgroup-subgroup link
|
|
counter = 0;
|
|
for (var _i4 = 0; _i4 < n; _i4 += 1) {
|
|
var _di = groupIndex[_i4];
|
|
s = subgroupIndex[_di].length;
|
|
for (j = 0; j < s; j += 1) {
|
|
var outerGroup = subgroups[counter];
|
|
var innerTerm = outerGroup.innername;
|
|
// Find the correct inner object based on the name
|
|
var innerGroup = searchTerm(innerTerm, 'name', uniqueInner);
|
|
if (outerGroup.value) {
|
|
looms.push({ inner: innerGroup, outer: outerGroup });
|
|
} // if
|
|
counter += 1;
|
|
} // for j
|
|
} // for i
|
|
|
|
return sortLooms ? looms.sort(sortLooms) : looms;
|
|
} // loomLayout
|
|
|
|
function searchTerm(term, property, arrayToSearch) {
|
|
for (var i = 0; i < arrayToSearch.length; i += 1) {
|
|
if (arrayToSearch[i][property] === term) {
|
|
return arrayToSearch[i];
|
|
} // if
|
|
} // for i
|
|
return null;
|
|
} // searchTerm
|
|
|
|
loomLayout.padAngle = function (_) {
|
|
return arguments.length ? (padAngle = Math.max(0, _), loomLayout) : padAngle;
|
|
};
|
|
|
|
loomLayout.inner = function (_) {
|
|
return arguments.length ? (inner = _, loomLayout) : inner;
|
|
};
|
|
|
|
loomLayout.outer = function (_) {
|
|
return arguments.length ? (outer = _, loomLayout) : outer;
|
|
};
|
|
|
|
loomLayout.value = function (_) {
|
|
return arguments.length ? (value = _, loomLayout) : value;
|
|
};
|
|
|
|
loomLayout.heightInner = function (_) {
|
|
return arguments.length ? (heightInner = _, loomLayout) : heightInner;
|
|
};
|
|
|
|
loomLayout.widthInner = function (_) {
|
|
return arguments.length ? (widthInner = typeof _ === 'function' ? _ : constant(+_), loomLayout) : widthInner;
|
|
};
|
|
|
|
loomLayout.emptyPerc = function (_) {
|
|
return arguments.length ? (emptyPerc = _ < 1 ? Math.max(0, _) : Math.max(0, _ * 0.01), loomLayout) : emptyPerc;
|
|
};
|
|
|
|
loomLayout.sortGroups = function (_) {
|
|
return arguments.length ? (sortGroups = _, loomLayout) : sortGroups;
|
|
};
|
|
|
|
loomLayout.sortSubgroups = function (_) {
|
|
return arguments.length ? (sortSubgroups = _, loomLayout) : sortSubgroups;
|
|
};
|
|
|
|
loomLayout.sortLooms = function (_) {
|
|
return arguments.length ? (_ == null ? sortLooms = null : (sortLooms = compareValue(_))._ = _, loomLayout) : sortLooms && sortLooms._;
|
|
};
|
|
|
|
return loomLayout;
|
|
} // loom
|
|
|
|
/* global d3 */
|
|
|
|
function string() {
|
|
var slice = Array.prototype.slice;
|
|
var cos = Math.cos;
|
|
var sin = Math.sin;
|
|
var halfPi = Math.PI / 2;
|
|
var tau = Math.PI * 2;
|
|
|
|
var inner = function inner(d) {
|
|
return d.inner;
|
|
};
|
|
var outer = function outer(d) {
|
|
return d.outer;
|
|
};
|
|
var radius = function radius() {
|
|
return 100;
|
|
};
|
|
var groupStartAngle = function groupStartAngle(d) {
|
|
return d.groupStartAngle;
|
|
};
|
|
var startAngle = function startAngle(d) {
|
|
return d.startAngle;
|
|
};
|
|
var endAngle = function endAngle(d) {
|
|
return d.endAngle;
|
|
};
|
|
var x = function x(d) {
|
|
return d.x;
|
|
};
|
|
var y = function y(d) {
|
|
return d.y;
|
|
};
|
|
var offset = function offset(d) {
|
|
return d.offset;
|
|
};
|
|
var pullout = 50;
|
|
var thicknessInner = 0;
|
|
var context = null;
|
|
|
|
function stringLayout() {
|
|
var buffer = void 0;
|
|
|
|
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
|
|
args[_key] = arguments[_key];
|
|
}
|
|
|
|
var argv = slice.call(args);
|
|
var out = outer.apply(this, argv);
|
|
var inn = inner.apply(this, argv);
|
|
argv[0] = out;
|
|
var sr = +radius.apply(this, argv);
|
|
var sa0 = startAngle.apply(this, argv) - halfPi;
|
|
var sga0 = groupStartAngle.apply(this, argv) - halfPi;
|
|
var sa1 = endAngle.apply(this, argv) - halfPi;
|
|
var sx0 = sr * cos(sa0);
|
|
var sy0 = sr * sin(sa0);
|
|
var sx1 = sr * cos(sa1);
|
|
var sy1 = sr * sin(sa1);
|
|
argv[0] = inn;
|
|
// 'tr' is assigned a value but never used
|
|
// const tr = +radius.apply(this, (argv));
|
|
var tx = x.apply(this, argv);
|
|
var ty = y.apply(this, argv);
|
|
var toffset = offset.apply(this, argv);
|
|
var xco = void 0;
|
|
var yco = void 0;
|
|
var xci = void 0;
|
|
var yci = void 0;
|
|
|
|
// Does the group lie on the left side;
|
|
var leftHalf = sga0 + halfPi > Math.PI && sga0 + halfPi < tau;
|
|
// If the group lies on the other side, switch the inner point offset
|
|
if (leftHalf) toffset = -toffset;
|
|
tx += toffset;
|
|
// And the height of the end point
|
|
var theight = leftHalf ? -thicknessInner : thicknessInner;
|
|
|
|
if (!context) {
|
|
buffer = d3.path();
|
|
context = buffer;
|
|
}
|
|
|
|
// Change the pullout based on where the stringLayout is
|
|
var pulloutContext = (leftHalf ? -1 : 1) * pullout;
|
|
sx0 += pulloutContext;
|
|
sx1 += pulloutContext;
|
|
// Start at smallest angle of outer arc
|
|
context.moveTo(sx0, sy0);
|
|
// Circular part along the outer arc
|
|
context.arc(pulloutContext, 0, sr, sa0, sa1);
|
|
// From end outer arc to center (taking into account the pullout)
|
|
xco = d3.interpolateNumber(pulloutContext, sx1)(0.5);
|
|
yco = d3.interpolateNumber(0, sy1)(0.5);
|
|
if (!leftHalf && sx1 < tx || leftHalf && sx1 > tx) {
|
|
// If the outer point lies closer to the center than the inner point
|
|
xci = tx + (tx - sx1) / 2;
|
|
yci = d3.interpolateNumber(ty + theight / 2, sy1)(0.5);
|
|
} else {
|
|
xci = d3.interpolateNumber(tx, sx1)(0.25);
|
|
yci = ty + theight / 2;
|
|
} // else
|
|
context.bezierCurveTo(xco, yco, xci, yci, tx, ty + theight / 2);
|
|
// Draw a straight line up/down (depending on the side of the circle)
|
|
context.lineTo(tx, ty - theight / 2);
|
|
// From center (taking into account the pullout) to start of outer arc
|
|
xco = d3.interpolateNumber(pulloutContext, sx0)(0.5);
|
|
yco = d3.interpolateNumber(0, sy0)(0.5);
|
|
if (!leftHalf && sx0 < tx || leftHalf && sx0 > tx) {
|
|
// If the outer point lies closer to the center than the inner point
|
|
xci = tx + (tx - sx0) / 2;
|
|
yci = d3.interpolateNumber(ty - theight / 2, sy0)(0.5);
|
|
} else {
|
|
xci = d3.interpolateNumber(tx, sx0)(0.25);
|
|
yci = ty - theight / 2;
|
|
} // else
|
|
context.bezierCurveTo(xci, yci, xco, yco, sx0, sy0);
|
|
// Close path
|
|
context.closePath();
|
|
|
|
if (buffer) {
|
|
context = null;
|
|
return '' + buffer || null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
stringLayout.radius = function (_) {
|
|
return arguments.length ? (radius = typeof _ === 'function' ? _ : constant(+_), stringLayout) : radius;
|
|
};
|
|
|
|
stringLayout.groupStartAngle = function (_) {
|
|
return arguments.length ? (groupStartAngle = typeof _ === 'function' ? _ : constant(+_), stringLayout) : groupStartAngle;
|
|
};
|
|
|
|
stringLayout.startAngle = function (_) {
|
|
return arguments.length ? (startAngle = typeof _ === 'function' ? _ : constant(+_), stringLayout) : startAngle;
|
|
};
|
|
|
|
stringLayout.endAngle = function (_) {
|
|
return arguments.length ? (endAngle = typeof _ === 'function' ? _ : constant(+_), stringLayout) : endAngle;
|
|
};
|
|
|
|
stringLayout.x = function (_) {
|
|
return arguments.length ? (x = _, stringLayout) : x;
|
|
};
|
|
|
|
stringLayout.y = function (_) {
|
|
return arguments.length ? (y = _, stringLayout) : y;
|
|
};
|
|
|
|
stringLayout.offset = function (_) {
|
|
return arguments.length ? (offset = _, stringLayout) : offset;
|
|
};
|
|
|
|
stringLayout.thicknessInner = function (_) {
|
|
return arguments.length ? (thicknessInner = _, stringLayout) : thicknessInner;
|
|
};
|
|
|
|
stringLayout.inner = function (_) {
|
|
return arguments.length ? (inner = _, stringLayout) : inner;
|
|
};
|
|
|
|
stringLayout.outer = function (_) {
|
|
return arguments.length ? (outer = _, stringLayout) : outer;
|
|
};
|
|
|
|
stringLayout.pullout = function (_) {
|
|
return arguments.length ? (pullout = _, stringLayout) : pullout;
|
|
};
|
|
|
|
stringLayout.context = function (_) {
|
|
return arguments.length ? (context = _ == null ? null : _, stringLayout) : context;
|
|
};
|
|
|
|
return stringLayout;
|
|
}
|
|
|
|
exports.loom = loom;
|
|
exports.string = string;
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
})));
|
|
//# sourceMappingURL=d3-loom.js.map
|
|
|