added starplot, fixed barchart, added annotations

master
parent 19472c9fa4
commit f29c758ac8
  1. 26
      css/style.css
  2. 20
      index.html
  3. 270
      js/k-nearest.js
  4. 366
      js/tsne_vis.js
  5. 2
      modules/d3-annotations/.gitignore
  6. 299
      modules/d3-annotations/d3-annotator.js
  7. 2
      modules/d3-star/.gitignore
  8. 272
      modules/d3-star/d3-starPlot.js

@ -117,7 +117,7 @@ cursor: default;
#knnBarChart {
width: 50vw;
height: 4.2vw;
margin-top: 3.6vw;
margin-top: 4.4vw;
border: 1px solid black;
position: absolute;
display: block;
@ -177,7 +177,7 @@ svg#legend4 {
#ThumbNailsList{
border: 1px solid black;
width: 15.4vw;
height: 8vw;
height: 13vw;
padding: 10px 10px 10px 10px;
}
@ -298,4 +298,26 @@ rect {
.bar {
/*shape-rendering: crispEdges;*/
}
.annotation circle {
fill: none;
stroke: black;
}
.annotation path {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
/* Styling of the main SVG behind canvas */
#SvgAnnotator {
position: absolute;
z-index: 3;
}
#toggleAnnotator {
color: darkgray;
}

@ -6,8 +6,9 @@
<!-- Importing D3 module -->
<script src='./modules/d3v3/d3.min.js'></script>
<script src='./modules/d3/d3.min.js'></script>
<script src='./modules/d3-star/d3-starPlot.js'></script>
<script src='./modules/d3-annotations/d3-annotator.js'></script>
<script src="./modules/d3-tip/tip.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.13.0/d3-legend.js"></script>
<script src="./modules/savage.toggle-switch.js" type="text/javascript"></script>
@ -17,7 +18,6 @@
<!-- Basic scripts -->
<script src="./js/tsne.js"></script>
<script src="./js/k-nearest.js"></script>
<script src="./js/checkbox.js"></script>
<script src="./js/data_form_handler.js"></script>
<script src="./lasso.js"></script>
@ -62,6 +62,7 @@
</div>
</div>
<div class="col-md-6">
<svg id="SvgAnnotator"></svg>
<svg id="modtSNEcanvas_svg"></svg>
<canvas id = "modtSNEcanvas" ></canvas>
</div>
@ -84,7 +85,10 @@
<input id="param-corr" type="range" min="0" max="750" value="150", step="50">
<output for="param-corr" id="param-corr-value">150</output>
</div>
Type the text of the form: <textarea type="text" id="comment" name="textforAnnotator">Please, provide your comment.</textarea>
</div>
<p><div id="toggleAnnotator"><button class="btn btn-primary btn-block" onclick="setAnnotator();">Add a new comment</button></div></p>
<p><div id="continue"><button class="btn btn-primary btn-block" onclick="setContinue();">Continue with the analysis</button></div></p>
</div>
</div>
</div>
@ -162,19 +166,23 @@
<output for="param-transform" id="param-transform-value"></output>
</div>
<p><div id="run-button"><button class="btn btn-primary btn-block" onclick="getData();">Run New tSNE</button></div></p>
<div id="cost"></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3 col-md-offset-6">
<div id="starPlot"></div>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">History Thumbnails</h3>
<h3 class="panel-title">Switch Mode</h3>
</div>
<div class="panel-body">
<div id="cost"></div>
<div id="datasetDetails"></div>
<div id="toggleLassoAndLine"></div>
</div>
</div>

@ -1,270 +0,0 @@
function kNearestNeighbors(k, points, points2d) {
var averagekNN = 0;
var Distances = [];
var Distances2d = [];
//var sortedDistances = [];
var point = points;
var point2d = points2d;
/*
* Loop through our nodes and look for unknown types.
*/
for (var i=0; i<point.length; i++){
/*
if (this.nodes.hasOwnProperty(i)) {
if ( ! this.nodes[i].type) {*/
/*
* If the node is an unknown type, clone the nodes list and then measure distances.
*/
/* Clone nodes *//*
this.nodes[i].neighbors = [];
for (var j in this.nodes) {
if ( ! this.nodes[j].type)
continue;
this.nodes[i].neighbors.push( new KNN.Item(this.nodes[j]) );
}*/
/* Measure distances */
Distances[i] = measureDistances(point[i],points);
Distances2d[i] = measureDistances(point2d[i],points2d);
Distances[i].sort();
Distances2d[i].sort();
/* Sort by distance */
//sortByDistance();
/* Guess type */
//this.type = this.nodes[i].guessType(this.k);
}
//console.log(Distances);
sum = LimitKNeighbor(Distances, k);
sum2d = LimitKNeighbor(Distances2d, k);
averagekNN = Math.round((sum2d / sum) * 100) / 100;
return averagekNN;
}
function measureDistances(point,points) {
var neighborDistances = [];
var checkForSame = [];
for (var j=0; j<points.length; j++){
neighborDistances[j] = Math.sqrt( (point.x-points[j].x)*(point.x-points[j].x) + (point.y-points[j].y)*(point.y-points[j].y) );
}
//neighborDistances = sortByDistance(neighborDistances);
return neighborDistances;
}
function LimitKNeighbor(Distances, k) {
var DistancesSliced = [];
var sum = 0;
for (var i = 0; i < Distances.length; i++) {
//for (var j = 0; j < Distances.length; j++) {
DistancesSliced[i] = Distances[i].slice(0,k);
for (var j = 0 ; j < k ; j++){
sum = DistancesSliced[i][j] + sum;
}
//sum = DistancesSliced[i] + sum;
//}
}
//console.log(sum);
return sum;
//console.log(DistancesBetweenAllThePoints[0][0]);
//console.log(DistancesBetweenAllThePoints);
/*
var guess = {type: false, count: 0};
for (var type in types) {
if (types[type] > guess.count) {
guess.type = type;
guess.count = types[type];
}
}
this.guess = guess;
return types;
};
calculateRanges = function() {
this.areas = {min: 1000000, max: 0};
this.rooms = {min: 1000000, max: 0};
for (var i in this.nodes) {
if (this.nodes.hasOwnProperty(i)) {
if (this.nodes[i].rooms < this.rooms.min) {
this.rooms.min = this.nodes[i].rooms;
}
if (this.nodes[i].rooms > this.rooms.max) {
this.rooms.max = this.nodes[i].rooms;
}
if (this.nodes[i].area < this.areas.min) {
this.areas.min = this.nodes[i].area;
}
if (this.nodes[i].area > this.areas.max) {
this.areas.max = this.nodes[i].area;
}
}
}
};*/
}
/*
function findNearest(point) {
// TODO: make this more efficient by not recalculating quadtree at
// each call of findNearest()
// Extract points from the data array
//points = data.map(function(d) { return [x(d), y(d)]; });
// Add quadtree info to the points
//nodes = quadtreeify(points);
// Flag k-nearest points by adding `selected` property set to `true`
kNearest(new Array(nodes), [], point);
// Return nearest points along with indices from origianl `data` array
return points
.map(function(d, i) {
var datum = [d[0], d[1]];
datum.i = i;
return d.selected ? datum : null;
})
.filter(function(d) { return d !== null; });
}
findNearest.extent = function(_) {
if (!arguments.length) return extent;
extent = _;
//quadtree.extent(extent);
return findNearest;
};
findNearest.data = function(_) {
if (!arguments.length) return data;
data = _;
return findNearest;
};
findNearest.k = function(_) {
if (!arguments.length) return k;
k = _;
return findNearest;
};
findNearest.x = function(_) {
if (!arguments.length) return x;
x = _;
return findNearest;
};
findNearest.y = function(_) {
if (!arguments.length) return y;
y = _;
return findNearest;
};
return findNearest;
// Add quadtree information to each point (i.e., rectangles, depth, ...)
function quadtreeify(points) {
var nodes = quadtree(points);
nodes.depth = 0;
nodes.visit(function(node, x1, y1, x2, y2) {
node.x1 = x1;
node.y1 = y1;
node.x2 = x2;
node.y2 = y2;
for (var i = 0; i < 4; i++) {
if (node.nodes[i]) node.nodes[i].depth = node.depth + 1;
}
});
return nodes;
}
// calculate the euclidean distance of two points with coordinates a(ax, ay) and b(bx, by)
function euclideanDistance(ax, ay, bx, by) {
return Math.sqrt(Math.pow(ax - bx, 2) + Math.pow(ay - by, 2));
}
// calculate minimum distance between search point rectangles
function minDistance(x, y, x1, y1, x2, y2) {
var dx1 = x - x1,
dx2 = x - x2,
dy1 = y - y1,
dy2 = y - y2;
// x is between x1 and x2
if (dx1 * dx2 < 0) {
// (x, y) is inside the rectangle
if (dy1 * dy2 < 0) {
return 0; // return 0 as a point in the rectangle
}
return Math.min(Math.abs(dy1), Math.abs(dy2));
}
// y is between y1 and y2 (and not inside rectangle)
if (dy1 * dy2 < 0) {
return Math.min(Math.abs(dx1), Math.abs(dx2));
}
return Math.min(
Math.min(euclideanDistance(x,y,x1,y1), euclideanDistance(x,y,x2,y2)),
Math.min(euclideanDistance(x,y,x1,y2), euclideanDistance(x,y,x2,y1))
);
}
// Find the nodes within the specified rectangle (used recursively)
function kNearest(bestQueue, resultQueue, point) {
var x = point[0],
y = point[1];
// sort children according to their minDistance/euclideanDistance to search point
bestQueue.sort(function(a, b) {
// add minDistance to nodes if not there already
[a, b].forEach(function(d) {
if (d.minDistance === undefined) {
d.scanned = true;
if (d.leaf) {
d.point.scanned = true;
d.minDistance = euclideanDistance(x, y, d.x, d.y);
}
else {
d.minDistance = minDistance(x, y, d.x1, d.y1, d.x2, d.y2);
}
}
});
return b.minDistance - a.minDistance;
});
// add nearest leafs (if any)
for (var i = bestQueue.length - 1; i >= 0; i--) {
var elem = bestQueue[i];
if (elem.leaf) {
elem.point.selected = true;
bestQueue.pop();
resultQueue.push(elem);
} else { break; }
if (resultQueue.length >= k) break;
}
// check if enough points found
if (resultQueue.length >= k || bestQueue.length == 0) { return; }
else {
// ...otherwise add child nodes to bestQueue and recurse
var visitedNode = bestQueue.pop();
visitedNode.visited = true;
visitedNode.nodes.forEach(function(d) {
bestQueue.push(d);
});
kNearest(bestQueue, resultQueue, point);
}
}
*/

@ -1,6 +1,6 @@
// t-SNE.js object and other global variables
var toggleValue = false; var k; var points = []; var all_fields; var pointsbeta = [];
var toggleValue = false; var k; var points = []; var all_fields; var pointsbeta = []; var KNNEnabled = true;
// These are the dimensions for the square shape of the main panel\
var dimensions = document.getElementById('modtSNEcanvas').offsetWidth;
@ -73,6 +73,57 @@ function setToggle(toggleVal){
toggleValue = toggleVal;
}
function setContinue(){
d3v3.select("#SvgAnnotator").style("z-index", 2);
}
function setAnnotator(){
var viewport2 = getViewport();
var vw2 = viewport2[0];
var vh2 = viewport2[1];
var textarea = document.getElementById("comment").value;
var annotations = [
{
"cx": 232,
"cy": 123,
"r": 103,
"text": textarea,
"textOffset": [
114,
88
]
}
];
var ringNote = d3v3.ringNote()
.draggable(true);
var svgAnnotator = d3v3.select("#SvgAnnotator")
.attr("width", vw2 * 0.5)
.attr("height", vh2 * 0.872)
.style("z-index", 3);
var gAnnotations = svgAnnotator.append("g")
.attr("class", "annotations")
.call(ringNote, annotations);
// Styling individual annotations based on bound data
gAnnotations.selectAll(".annotation circle")
.classed("shaded", function(d) { return d.shaded; });
// Hide or show the controls
var draggable = true;
d3.select("input")
.on("change", function() {
ringNote.draggable(draggable = !draggable);
gAnnotations
.call(ringNote, annotations)
.selectAll(".annotation circle")
.classed("shaded", function(d) { return d.shaded; });
});
}
// function that executes after data is successfully loaded
function init(data, results_all, fields) {
@ -88,7 +139,7 @@ function init(data, results_all, fields) {
tsne = new tsnejs.tSNE(opt);
final_dataset = data;
dataFeatures = results_all;
var temp, object;
var object;
for (let k = 0; k < dataFeatures.length; k++){
ArrayContainsDataFeatures.push(Object.values(dataFeatures[k]).concat(k));
object = [];
@ -99,7 +150,7 @@ function init(data, results_all, fields) {
});
ArrayContainsDataFeaturesCleared.push(object);
}
$("#datasetDetails").html("Number of Dimensions: " + ArrayContainsDataFeaturesCleared[0].length + ", Number of Samples: " + final_dataset.length);
dists = computeDistances(data, document.getElementById("param-distance").value, document.getElementById("param-transform").value);
tsne.initDataDist(dists);
all_labels = [];
@ -260,12 +311,11 @@ function updateEmbedding() {
}
return obj;
}
if (step_counter == 1 || step_counter == max_counter){
if (step_counter == max_counter){
ShepardHeatMap();
OverviewtSNE(points);
BetatSNE(points);
}
OverviewtSNE(points);
BetatSNE(points);
//CosttSNE(points);
}
function ShepardHeatMap () {
@ -285,12 +335,14 @@ function ShepardHeatMap () {
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
dists2d = computeDistances(points2d, document.getElementById("param-distance").value, document.getElementById("param-transform").value);
var dist_list2d = [];
var dist_list = [];
for (var j=0; j<dists2d.length; j++){
dists2d[j] = dists2d[j].slice(0,j);
dists[j] = dists[j].slice(0,j);
}
for (var i=0; i<dists2d.length; i++){
for (var j=0; j<dists2d.length; j++){
let singleObj = {};
@ -305,9 +357,6 @@ dist_list2d = dist_list2d.sort();
dist_list = dist_list.sort();
dist_list2d = dist_list2d.filter(function(val){ return val!==undefined; });
dist_list = dist_list.filter(function(val){ return val!==undefined; });
d3.tsv("./modules/heat.tsv").then(function(data) {
data.forEach(function(d) {
@ -524,7 +573,6 @@ function OverviewtSNE(points){
else{
var colorScale = d3.scaleOrdinal(d3.schemeCategory10).domain(all_labels);
}
if (step_counter == 1){
d3.select("#legend3").select("svg").remove();
var svg = d3.select("#legend3").append("svg");
@ -539,7 +587,7 @@ function OverviewtSNE(points){
svg.select(".legendOrdinal")
.call(legendOrdinal);
}
let vertices = [];
let colors = [];
@ -711,7 +759,7 @@ function redraw(repoints){
function handleLassoEnd(lassoPolygon) {
var countLassoFalse = 0;
KNNEnabled = true;
if (toggleValue == false){
for (var i = 0 ; i < points.length ; i ++) {
x = points[i].x;
@ -742,6 +790,7 @@ function handleLassoEnd(lassoPolygon) {
function handleLassoStart(lassoPolygon) {
if (toggleValue == true){
} else{
KNNEnabled = false;
for (var i = 0 ; i < points.length ; i ++) {
points[i].selected = true;
points2d[i].selected = true;
@ -982,10 +1031,10 @@ function click(){
var main_margin = {top: 0, right: 10, bottom: 30, left: 100},
main_width = 500 - main_margin.left - main_margin.right,
main_height = 400 - main_margin.top - main_margin.bottom;
main_height = 320 - main_margin.top - main_margin.bottom;
var mini_margin = {top: 0, right: 10, bottom: 30, left: 10},
mini_height = 400 - mini_margin.top - mini_margin.bottom;
mini_height = 320 - mini_margin.top - mini_margin.bottom;
mini_width = 100 - mini_margin.left - mini_margin.right;
svg = d3v3.select("#correlation").attr("class", "svgWrapper")
@ -1498,7 +1547,6 @@ function BetatSNE(points){
.style('position', 'absolute')
.style('top', 0)
.style('left', 0);
var lassoInstance = lasso()
.on('end', handleLassoEnd)
.on('start', handleLassoStart);
@ -1507,6 +1555,7 @@ function BetatSNE(points){
}
}
}
var canvas = document.getElementById('modtSNEcanvas');
var gl = canvas.getContext('webgl');
// If we don't have a GL context, give up now
@ -1921,51 +1970,264 @@ function BetatSNE(points){
//Draw the triangle
gl.drawArrays(gl.POINTS, 0, points.length);
/*
selectedPoints = [];
selectedPoints2d = [];
var findNearestTable = [];
for (let m=0; m<points.length; m++){
if (points[m].selected == true){
selectedPoints.push(points[m]);
selectedPoints2d.push(points2d[m]);
}
}*/
/*
for (k=2; k < 9; k++){
var findNearest = kNearestNeighbors(k, selectedPoints,points2d);
if (isNaN(findNearest)){
findNearest = 0;
}
findNearestTable.push(findNearest * 65);
}
if (KNNEnabled == true && selectedPoints.length != 0){
var distsFull = dists;
var dists2dFull = dists2d;
for (var i=0; i<dists.length; i++){
for (var j=0; j<dists.length; j++){
if(dists[i][j] != null) {
distsFull[j][i] = dists[i][j];
dists2dFull[j][i] = dists2d[i][j];
}
}
}
var indexOrder = [];
var indexOrder2d = [];
var indices = new Array(selectedPoints.length);
var indices2d = new Array(selectedPoints.length);
var barPadding = 5;
if (step_counter != 1){
d3.select("#knnBarChart").selectAll("rect").remove();
var findNearest;
var counter1;
var counter2;
var temp = [];
var temp2 = [];
var viewport = getViewport();
var vw = viewport[0] * 0.5;
var vh = viewport[1] * 0.042;
var factor = Math.log10(points.length) * 4;
if (factor == 0){
factor = 1;
}
var maxKNN = Math.ceil(points.length / factor);
var svg = d3.select('#knnBarChart')
.attr("class", "bar-chart");
selectedPoints.sort(function(a, b) {
return parseFloat(a.id) - parseFloat(b.id);
});
for (k=maxKNN; k>1; k--){
findNearest = 0;
var indexOrderSliced = [];
var indexOrderSliced2d = [];
var count1 = new Array(selectedPoints.length).fill(0);
var count2 = new Array(selectedPoints.length).fill(0);
counter1 = 0;
counter2 = 0;
for (var i=0; i<selectedPoints.length; i++){
if (k == maxKNN){
temp[i] = 0;
temp2[i] = 0;
// temporary array holds objects with position and sort-value
indices[i] = dists[i].map(function(el, i) {
return [ i, el ];
})
var index = indices[i].indexOf(selectedPoints[i].id);
if (index > -1) {
indices[i].splice(index, 1);
}
// sorting the mapped array containing the reduced values
indices[i].sort(function(a, b) {
if (a[1] > b[1]) {
return 1;
}
if (a[1] < b[1]) {
return -1;
}
return 0;
});
indexOrder[i] = indices[i].map(function(value) { return value[0]; });
// temporary array holds objects with position and sort-value
indices2d[i] = dists2d[i].map(function(el, i) {
return [ i, el ];
})
var index2d = indices2d[i].indexOf(selectedPoints[i].id);
if (index2d > -1) {
indices2d[i].splice(index2d, 1);
}
// sorting the mapped array containing the reduced values
indices2d[i].sort(function(a, b) {
if (a[1] > b[1]) {
return 1;
}
if (a[1] < b[1]) {
return -1;
}
return 0;
});
indexOrder2d[i] = indices2d[i].map(function(value) { return value[0]; });
}
indexOrderSliced[i] = indexOrder[i].slice(0,k);
indexOrderSliced2d[i] = indexOrder2d[i].slice(0,k);
var barWidth = (svgWidth / findNearestTable.length);
var knnBarChartSVG = svg.selectAll("rect")
.data(findNearestTable)
.enter()
.append("rect")
.attr("y", function(d) {
return Math.round(svgHeight - d)
})
.attr("height", function(d) {
for (var m=0; m < indexOrderSliced2d[i].length; m++){
if (indexOrderSliced[i].includes(indexOrderSliced2d[i][m])){
count1[i] = count1[i] + 1;
temp[i] = temp[i] + 1;
}
if(indexOrderSliced[i][m] == indexOrderSliced2d[i][m]){
count2[i] = count2[i] + 1;
temp2[i] = temp2[i] + 1;
}
}
if (count1[i] != 0){
counter1 = (count1[i] / temp[i]) + counter1;
}
if (count2[i] != 0){
counter2 = (count2[i] / temp2[i]) + counter2;
}
}
sumUnion = counter1 / selectedPoints.length;
sumIntersection = counter2 / selectedPoints.length;
if (sumUnion == 0){
findNearest = 0;
} else{
findNearest = sumIntersection / sumUnion;
}
if (isNaN(findNearest)){
findNearest = 0;
}
findNearestTable.push(findNearest * vh * 2);
}
findNearestTable.reverse();
var barPadding = 5;
d3.select("#knnBarChart").selectAll("rect").remove();
var svg2 = d3.select('#knnBarChart')
.attr("class", "bar-chart");
var barWidth = (vw / findNearestTable.length);
var knnBarChartSVG = svg2.selectAll("rect")
.data(findNearestTable)
.enter()
.append("rect")
.attr("y", function(d) {
return Math.round(vh*2 - d)
})
.attr("height", function(d) {
return d;
})
.attr("width", barWidth - barPadding)
.attr("transform", function (d, i) {
var translate = [barWidth * i, 0];
return "translate("+ translate +")";
});*/
})
.attr("width", barWidth - barPadding)
.attr("transform", function (d, i) {
var translate = [barWidth * i, 0];
return "translate("+ translate +")";
});
}
d3.select("#starPlot").selectAll('g').remove();
if(selectedPoints.length <= 10){
var viewport3 = getViewport();
var vw3 = viewport3[0] * 0.2;
var margin = {top: 50, right: 100, bottom: 80, left: 150},
width = Math.min(vw3, window.innerWidth - 10) - margin.left - margin.right,
height = Math.min(width, window.innerHeight - margin.top - margin.bottom - 20);
var FeatureWise = [];
for (var j=0; j<Object.values(dataFeatures[0]).length; j++){
for (var i=0;i<dataFeatures.length;i++){
if (!isNaN(Object.values(dataFeatures[i])[j])){
FeatureWise.push(Object.values(dataFeatures[i])[j]);
}
}
}
var sum = new Array(Object.values(dataFeatures[0]).length).fill(0);
for (var j=0; j<Object.values(dataFeatures[0]).length; j++){
var FeatureWiseSliced = FeatureWise.slice(0+(j*dataFeatures.length),dataFeatures.length+j*dataFeatures.length);
for (var i=0; i<FeatureWiseSliced.length; i++){
sum[j] = FeatureWiseSliced[i] + sum[j];
}
}
var wrapData = [];
for (var i=0; i<selectedPoints.length; i++){
var data = [];
for (var j=0; j< Object.keys(dataFeatures[selectedPoints[i].id]).length; j++){
if (!isNaN(Object.values(dataFeatures[i])[j])){
if (Object.keys(dataFeatures[i])[j] == "name") {
} else{
data.push({axis:Object.keys(dataFeatures[i])[j],value:Math.abs(Object.values(dataFeatures[i])[j]*100/sum[j])});
}
}
}
wrapData.push(data);
}
//////////////////////////////////////////////////////////////
//////////////////// Draw the Chart //////////////////////////
//////////////////////////////////////////////////////////////
var color = d3v3.scale.category10()
var radarChartOptions = {
w: width,
h: height,
margin: margin,
levels: 10,
roundStrokes: true,
color: color
};
//Call function to draw the Radar chart
RadarChart("#starPlot", wrapData, radarChartOptions);
}
}
function getViewport() {
var viewPortWidth;
var viewPortHeight;
// the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight
if (typeof window.innerWidth != 'undefined') {
viewPortWidth = window.innerWidth,
viewPortHeight = window.innerHeight
}
// IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
else if (typeof document.documentElement != 'undefined'
&& typeof document.documentElement.clientWidth !=
'undefined' && document.documentElement.clientWidth != 0) {
viewPortWidth = document.documentElement.clientWidth,
viewPortHeight = document.documentElement.clientHeight
}
// older versions of IE
else {
viewPortWidth = document.getElementsByTagName('body')[0].clientWidth,
viewPortHeight = document.getElementsByTagName('body')[0].clientHeight
}
return [viewPortWidth, viewPortHeight];
}
/*
function CosttSNE(points){
@ -2180,13 +2442,3 @@ function CosttSNE(points){
}
*/
// This function calls KNN and draw a bar chart.
function knnBarChart(){
}
// This function draws the lines for the best distributions.
function schemaCompare(){
}

@ -0,0 +1,2 @@
node_modules

@ -0,0 +1,299 @@
d3v3.ringNote = function() {
var draggable = false,
controlRadius = 15;
var dragCenter = d3v3.behavior.drag()
.origin(function(d) { return { x: 0, y: 0}; })
.on("drag", dragmoveCenter);
var dragRadius = d3v3.behavior.drag()
.origin(function(d) { return { x: 0, y: 0 }; })
.on("drag", dragmoveRadius);
var dragText = d3v3.behavior.drag()
.origin(function(d) { return { x: 0, y: 0 }; })
.on("drag", dragmoveText);
var path = d3v3.svg.line();
function draw(selection, annotation) {
selection.selectAll(".ring-note").remove();
var gRingNote = selection.selectAll(".ring-note")
.data(annotation)
.enter().append("g")
.attr("class", "ring-note")
.attr("transform", function(d) {
return "translate(" + d.cx + "," + d.cy + ")";
});
var gAnnotation = gRingNote.append("g")
.attr("class", "annotation");
var circle = gAnnotation.append("circle")
.attr("r", function(d) { return d.r; });
var line = gAnnotation.append("path")
.call(updateLine);
var text = gAnnotation.append("text")
.call(updateText);
if (draggable) {
var gControls = gRingNote.append("g")
.attr("class", "controls");
// Draggable circle that moves the circle's location
var center = gControls.append("circle")
.attr("class", "center")
.call(styleControl)
.call(dragCenter);
// Draggable circle that changes the circle's radius
var radius = gControls.append("circle")
.attr("class", "radius")
.attr("cx", function(d) { return d.r; })
.call(styleControl)
.call(dragRadius);
// Make text draggble
text
.style("cursor", "move")
.call(dragText);
}
return selection;
}
draw.draggable = function(_) {
if (!arguments.length) return draggable;
draggable = _;
return draw;
};
// Region in relation to circle, e.g., N, NW, W, SW, etc.
function getRegion(x, y, r) {
var px = r * Math.cos(Math.PI/4),
py = r * Math.sin(Math.PI/4);
var distance = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if (distance < r) {
return null;
}
else {
if (x > px) {
// East
if (y > py) return "SE";
if (y < -py) return "NE";
if (x > r) return "E";
return null;
}
else if (x < -px) {
// West
if (y > py) return "SW";
if (y < -py) return "NW";
if (x < -r) return "W";
return null;
}
else {
// Center
if (y > r) return "S";
if (y < -r) return "N";
}
}
}
function dragmoveCenter(d) {
var gRingNote = d3v3.select(this.parentNode.parentNode);
d.cx += d3v3.event.x;
d.cy += d3v3.event.y;
gRingNote
.attr("transform", function(d) {
return "translate(" + d.cx + "," + d.cy + ")";
});
}
function dragmoveRadius(d) {
var gRingNote = d3v3.select(this.parentNode.parentNode),
gAnnotation = gRingNote.select(".annotation"),
circle = gAnnotation.select("circle"),
line = gAnnotation.select("path"),
text = gAnnotation.select("text"),
radius = d3v3.select(this);
d.r += d3v3.event.dx;
circle.attr("r", function(d) { return d.r; });
radius.attr("cx", function(d) { return d.r; });
line.call(updateLine);
text.call(updateText);
}
function dragmoveText(d) {
var gAnnotation = d3v3.select(this.parentNode),
line = gAnnotation.select("path"),
text = d3v3.select(this);
d.textOffset[0] += d3v3.event.dx;
d.textOffset[1] += d3v3.event.dy;
text.call(updateText);
line.call(updateLine);
}
function updateLine(selection) {
return selection.attr("d", function(d) {
var x = d.textOffset[0],
y = d.textOffset[1],
lineData = getLineData(x, y, d.r);
return path(lineData);
});
}
function getLineData(x, y, r) {
var region = getRegion(x, y, r);
if (region == null) {
// No line if text is inside the circle
return [];
}
else {
// Cardinal directions
if (region == "N") return [[0, -r], [0, y]];
if (region == "E") return [[r, 0], [x, 0]];
if (region == "S") return [[0, r], [0, y]];
if (region == "W") return [[-r, 0],[x, 0]];
var d0 = r * Math.cos(Math.PI/4),
d1 = Math.min(Math.abs(x), Math.abs(y)) - d0;
// Intermediate directions
if (region == "NE") return [[ d0, -d0], [ d0 + d1, -d0 - d1], [x, y]];
if (region == "SE") return [[ d0, d0], [ d0 + d1, d0 + d1], [x, y]];
if (region == "SW") return [[-d0, d0], [-d0 - d1, d0 + d1], [x, y]];
if (region == "NW") return [[-d0, -d0], [-d0 - d1, -d0 - d1], [x, y]];
}
}
function updateText(selection) {
return selection.each(function(d) {
var x = d.textOffset[0],
y = d.textOffset[1],
region = getRegion(x, y, d.r),
textCoords = getTextCoords(x, y, region);
d3v3.select(this)
.attr("x", textCoords.x)
.attr("y", textCoords.y)
.text(d.text)
.each(function(d) {
var x = d.textOffset[0],
y = d.textOffset[1],
textAnchor = getTextAnchor(x, y, region);
var dx = textAnchor == "start" ? "0.33em" :
textAnchor == "end" ? "-0.33em" : "0";
var dy = textAnchor !== "middle" ? ".33em" :
["NW", "N", "NE"].indexOf(region) !== -1 ? "-.33em" : "1em";
var orientation = textAnchor !== "middle" ? undefined :
["NW", "N", "NE"].indexOf(region) !== -1 ? "bottom" : "top";
d3v3.select(this)
.style("text-anchor", textAnchor)
.attr("dx", dx)
.attr("dy", dy)
.call(wrapText, d.textWidth || 960, orientation);
});
});
}
function getTextCoords(x, y, region) {
if (region == "N") return { x: 0, y: y };
if (region == "E") return { x: x, y: 0 };
if (region == "S") return { x: 0, y: y };
if (region == "W") return { x: x, y: 0 };
return { x: x, y: y };
}
function getTextAnchor(x, y, region) {
if (region == null) {
return "middle";
}
else {
// Cardinal directions
if (region == "N") return "middle";
if (region == "E") return "start";
if (region == "S") return "middle";
if (region == "W") return "end";
var xLonger = Math.abs(x) > Math.abs(y);
// Intermediate directions`
if (region == "NE") return xLonger ? "start" : "middle";
if (region == "SE") return xLonger ? "start" : "middle";
if (region == "SW") return xLonger ? "end" : "middle";
if (region == "NW") return xLonger ? "end" : "middle";
}
}
// Adapted from: https://bl.ocks.org/mbostock/7555321
function wrapText(text, width, orientation) {
text.each(function(d) {
var text = d3v3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 1,
lineHeight = 1.1, // ems
x = text.attr("x"),
dx = text.attr("dx"),
tspan = text.text(null).append("tspan").attr("x", x).attr("dx", dx);
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan")
.attr("x", x)
.attr("dx", dx)
.attr("dy", lineHeight + "em")
.text(word);
lineNumber++;
}
}
var dy;
if (orientation == "bottom") {
dy = -lineHeight * (lineNumber-1) - .33;
}
else if (orientation == "top") {
dy = 1;
}
else {
dy = -lineHeight * ((lineNumber-1) / 2) + .33;
}
text.attr("dy", dy + "em");
});
}
function styleControl(selection) {
selection
.attr("r", controlRadius)
.style("fill-opacity", "0")
.style("stroke", "black")
.style("stroke-dasharray", "3, 3")
.style("cursor", "move");
}
return draw;
};

@ -0,0 +1,2 @@
node_modules

@ -0,0 +1,272 @@
/////////////////////////////////////////////////////////
/////////////// The Radar Chart Function ////////////////
/////////////// Written by Nadieh Bremer ////////////////
////////////////// VisualCinnamon.com ///////////////////
/////////// Inspired by the code of alangrafu ///////////
/////////////////////////////////////////////////////////
function RadarChart(id, data, options) {
var cfg = {
w: 600, //Width of the circle
h: 600, //Height of the circle
margin: {top: 20, right: 20, bottom: 20, left: 20}, //The margins of the SVG
levels: 3, //How many levels or inner circles should there be drawn
maxValue: 0, //What is the value that the biggest circle will represent
labelFactor: 1.25, //How much farther than the radius of the outer circle should the labels be placed
wrapWidth: 60, //The number of pixels after which a label needs to be given a new line
opacityArea: 0.35, //The opacity of the area of the blob
dotRadius: 4, //The size of the colored circles of each blog
opacityCircles: 0.1, //The opacity of the circles of each blob
strokeWidth: 2, //The width of the stroke around each blob
roundStrokes: false, //If true the area and stroke will follow a round path (cardinal-closed)
color: d3v3.scale.category10() //Color function
};
//Put all of the options into a variable called cfg
if('undefined' !== typeof options){
for(var i in options){
if('undefined' !== typeof options[i]){ cfg[i] = options[i]; }
}//for i
}//if
//If the supplied maxValue is smaller than the actual one, replace by the max in the data
var maxValue = Math.max(cfg.maxValue, d3v3.max(data, function(i){return d3v3.max(i.map(function(o){return o.value;}))}));
var allAxis = (data[0].map(function(i, j){return i.axis})), //Names of each axis
total = allAxis.length, //The number of different axes
radius = Math.min(cfg.w/2, cfg.h/2), //Radius of the outermost circle
Format = d3v3.format('%'), //Percentage formatting
angleSlice = Math.PI * 2 / total; //The width in radians of each "slice"
//Scale for the radius
var rScale = d3v3.scale.linear()
.range([0, radius])
.domain([0, maxValue]);
/////////////////////////////////////////////////////////
//////////// Create the container SVG and g /////////////
/////////////////////////////////////////////////////////
//Remove whatever chart with the same id/class was present before
d3v3.select(id).select("svg").remove();
//Initiate the radar chart SVG
var svg = d3v3.select(id).append("svg")
.attr("width", cfg.w + cfg.margin.left + cfg.margin.right)
.attr("height", cfg.h + cfg.margin.top + cfg.margin.bottom)
.attr("class", "radar"+id);
//Append a g element
var g = svg.append("g")
.attr("transform", "translate(" + (cfg.w/2 + cfg.margin.left) + "," + (cfg.h/2 + cfg.margin.top) + ")");
/////////////////////////////////////////////////////////
////////// Glow filter for some extra pizzazz ///////////
/////////////////////////////////////////////////////////
//Filter for the outside glow
var filter = g.append('defs').append('filter').attr('id','glow'),
feGaussianBlur = filter.append('feGaussianBlur').attr('stdDeviation','2.5').attr('result','coloredBlur'),
feMerge = filter.append('feMerge'),
feMergeNode_1 = feMerge.append('feMergeNode').attr('in','coloredBlur'),
feMergeNode_2 = feMerge.append('feMergeNode').attr('in','SourceGraphic');
/////////////////////////////////////////////////////////
/////////////// Draw the Circular grid //////////////////
/////////////////////////////////////////////////////////
//Wrapper for the grid & axes
var axisGrid = g.append("g").attr("class", "axisWrapper");
//Draw the background circles
axisGrid.selectAll(".levels")
.data(d3v3.range(1,(cfg.levels+1)).reverse())
.enter()
.append("circle")
.attr("class", "gridCircle")
.attr("r", function(d, i){return radius/cfg.levels*d;})
.style("fill", "#CDCDCD")
.style("stroke", "#CDCDCD")
.style("fill-opacity", cfg.opacityCircles)
.style("filter" , "url(#glow)");
//Text indicating at what % each level is
axisGrid.selectAll(".axisLabel")
.data(d3v3.range(1,(cfg.levels+1)).reverse())
.enter().append("text")
.attr("class", "axisLabel")
.attr("x", 4)
.attr("y", function(d){return -d*radius/cfg.levels;})
.attr("dy", "0.4em")
.style("font-size", "10px")
.attr("fill", "#737373")
.text(function(d,i) { return Format(maxValue * d/cfg.levels); });
/////////////////////////////////////////////////////////
//////////////////// Draw the axes //////////////////////
/////////////////////////////////////////////////////////
//Create the straight lines radiating outward from the center
var axis = axisGrid.selectAll(".axis")
.data(allAxis)
.enter()
.append("g")
.attr("class", "axis");
//Append the lines
axis.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", function(d, i){ return rScale(maxValue*1.1) * Math.cos(angleSlice*i - Math.PI/2); })
.attr("y2", function(d, i){ return rScale(maxValue*1.1) * Math.sin(angleSlice*i - Math.PI/2); })
.attr("class", "line")
.style("stroke", "white")
.style("stroke-width", "2px");
//Append the labels at each axis
axis.append("text")
.attr("class", "legend")
.style("font-size", "11px")
.attr("text-anchor", "middle")
.attr("dy", "0.35em")
.attr("x", function(d, i){ return rScale(maxValue * cfg.labelFactor) * Math.cos(angleSlice*i - Math.PI/2); })
.attr("y", function(d, i){ return rScale(maxValue * cfg.labelFactor) * Math.sin(angleSlice*i - Math.PI/2); })
.text(function(d){return d})
.call(wrap, cfg.wrapWidth);
/////////////////////////////////////////////////////////
///////////// Draw the radar chart blobs ////////////////
/////////////////////////////////////////////////////////
//The radial line function
var radarLine = d3v3.svg.line.radial()
.interpolate("linear-closed")
.radius(function(d) { return rScale(d.value); })
.angle(function(d,i) { return i*angleSlice; });
if(cfg.roundStrokes) {
radarLine.interpolate("cardinal-closed");
}
//Create a wrapper for the blobs
var blobWrapper = g.selectAll(".radarWrapper")
.data(data)
.enter().append("g")
.attr("class", "radarWrapper");
//Append the backgrounds
blobWrapper
.append("path")
.attr("class", "radarArea")
.attr("d", function(d,i) { return radarLine(d); })
.style("fill", function(d,i) { return cfg.color(i); })
.style("fill-opacity", cfg.opacityArea)
.on('mouseover', function (d,i){
//Dim all blobs
d3v3.selectAll(".radarArea")
.transition().duration(200)
.style("fill-opacity", 0.1);
//Bring back the hovered over blob
d3v3.select(this)
.transition().duration(200)
.style("fill-opacity", 0.7);
})
.on('mouseout', function(){
//Bring back all blobs
d3v3.selectAll(".radarArea")
.transition().duration(200)
.style("fill-opacity", cfg.opacityArea);
});
//Create the outlines
blobWrapper.append("path")
.attr("class", "radarStroke")
.attr("d", function(d,i) { return radarLine(d); })
.style("stroke-width", cfg.strokeWidth + "px")
.style("stroke", function(d,i) { return cfg.color(i); })
.style("fill", "none")
.style("filter" , "url(#glow)");
//Append the circles
blobWrapper.selectAll(".radarCircle")
.data(function(d,i) { return d; })
.enter().append("circle")
.attr("class", "radarCircle")
.attr("r", cfg.dotRadius)
.attr("cx", function(d,i){ return rScale(d.value) * Math.cos(angleSlice*i - Math.PI/2); })
.attr("cy", function(d,i){ return rScale(d.value) * Math.sin(angleSlice*i - Math.PI/2); })
.style("fill", function(d,i,j) { return cfg.color(j); })
.style("fill-opacity", 0.8);
/////////////////////////////////////////////////////////
//////// Append invisible circles for tooltip ///////////
/////////////////////////////////////////////////////////
//Wrapper for the invisible circles on top
var blobCircleWrapper = g.selectAll(".radarCircleWrapper")
.data(data)
.enter().append("g")
.attr("class", "radarCircleWrapper");
//Append a set of invisible circles on top for the mouseover pop-up
blobCircleWrapper.selectAll(".radarInvisibleCircle")
.data(function(d,i) { return d; })
.enter().append("circle")
.attr("class", "radarInvisibleCircle")
.attr("r", cfg.dotRadius*1.5)
.attr("cx", function(d,i){ return rScale(d.value) * Math.cos(angleSlice*i - Math.PI/2); })
.attr("cy", function(d,i){ return rScale(d.value) * Math.sin(angleSlice*i - Math.PI/2); })
.style("fill", "none")
.style("pointer-events", "all")
.on("mouseover", function(d,i) {
newX = parseFloat(d3v3.select(this).attr('cx')) - 10;
newY = parseFloat(d3v3.select(this).attr('cy')) - 10;
tooltip
.attr('x', newX)
.attr('y', newY)
.text(Format(d.value))
.transition().duration(200)
.style('opacity', 1);
})
.on("mouseout", function(){
tooltip.transition().duration(200)
.style("opacity", 0);
});
//Set up the small tooltip for when you hover over a circle
var tooltip = g.append("text")
.attr("class", "tooltip")
.style("opacity", 0);
/////////////////////////////////////////////////////////
/////////////////// Helper Function /////////////////////
/////////////////////////////////////////////////////////
//Taken from http://bl.ocks.org/mbostock/7555321
//Wraps SVG text
function wrap(text, width) {
text.each(function() {
var text = d3v3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.4, // ems
y = text.attr("y"),
x = text.attr("x"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}//wrap
}//RadarChart
Loading…
Cancel
Save