-
Notifications
You must be signed in to change notification settings - Fork 9
/
mds.js
102 lines (85 loc) · 3.46 KB
/
mds.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
(function(mds) {
"use strict";
/// given a matrix of distances between some points, returns the
/// point coordinates that best approximate the distances using
/// classic multidimensional scaling
mds.classic = function(distances, dimensions) {
dimensions = dimensions || 2;
// square distances
var M = numeric.mul(-0.5, numeric.pow(distances, 2));
// double centre the rows/columns
function mean(A) { return numeric.div(numeric.add.apply(null, A), A.length); }
var rowMeans = mean(M),
colMeans = mean(numeric.transpose(M)),
totalMean = mean(rowMeans);
for (var i = 0; i < M.length; ++i) {
for (var j =0; j < M[0].length; ++j) {
M[i][j] += totalMean - rowMeans[i] - colMeans[j];
}
}
// take the SVD of the double centred matrix, and return the
// points from it
var ret = numeric.svd(M),
eigenValues = numeric.sqrt(ret.S);
return ret.U.map(function(row) {
return numeric.mul(row, eigenValues).splice(0, dimensions);
});
};
/// draws a scatter plot of points, useful for displaying the output
/// from mds.classic etc
mds.drawD3ScatterPlot = function(element, xPos, yPos, labels, params) {
params = params || {};
var padding = params.padding || 32,
w = params.w || Math.min(720, document.documentElement.clientWidth - padding),
h = params.h || w,
xDomain = [Math.min.apply(null, xPos),
Math.max.apply(null, xPos)],
yDomain = [Math.max.apply(null, yPos),
Math.min.apply(null, yPos)],
pointRadius = params.pointRadius || 3;
if (params.reverseX) {
xDomain.reverse();
}
if (params.reverseY) {
yDomain.reverse();
}
var xScale = d3.scale.linear().
domain(xDomain)
.range([padding, w - padding]),
yScale = d3.scale.linear().
domain(yDomain)
.range([padding, h-padding]),
xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(params.xTicks || 7),
yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(params.yTicks || 7);
var svg = element.append("svg")
.attr("width", w)
.attr("height", h);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding + 2*pointRadius) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + (padding - 2*pointRadius) + ",0)")
.call(yAxis);
var nodes = svg.selectAll("circle")
.data(labels)
.enter()
.append("g");
nodes.append("circle")
.attr("r", pointRadius)
.attr("cx", function(d, i) { return xScale(xPos[i]); })
.attr("cy", function(d, i) { return yScale(yPos[i]); });
nodes.append("text")
.attr("text-anchor", "middle")
.text(function(d) { return d; })
.attr("x", function(d, i) { return xScale(xPos[i]); })
.attr("y", function(d, i) { return yScale(yPos[i]) - 2 *pointRadius; });
};
}(window.mds = window.mds || {}));