What I want to do
Multi-Dimension-Scaling is how to explain high dimension data as low dimension data.This method focus on distances between data. Try to implement MDS’s demo with cofee script.
Source Code
Model
class Point2D
constructor: (x, y, r, vx, vy, label, wnd_width, wnd_height, context) ->
@x = x
@y = y
@r = r
@vx = vx
@vy = vy
@label = label
@wnd_width = wnd_width
@wnd_height = wnd_height
@context = context
#
# Update poistion with velocity
#
update_position: ->
@x += @vx
@y += @vy
#
# Return x and y with array style
#
get_data: ->
return [@x, @y]
#
# Calculate distance between points
# @param p : Point2D class's instance
#
calc_distance: (p)->
return Math.pow(@x - p.x, 2)+ Math.pow(@y - p.y, 2)
#
# Draw point and label to canvas
#
draw: ->
@context.beginPath()
@context.strokeStyle = '#00F'
@context.fillStyle = 'green'
@context.arc(@x, @y, @r, 0, Math.PI * 2, false)
@context.fill()
@context.stroke()
@context.restore()
@context.beginPath()
@context.font = "18px 'MS Pゴシック'"
@context.fillStyle = "red"
@context.fillText(@label, @x, @y-(@r*2))
@context.restore()
#
# Draw point and label with scaling
#
draw_with_scaling: (max,min) ->
prevX = @x
prevY = @y
@x = (@x - min[0]) / (max[0] - min[0]) * @wnd_width
@y = (@y - min[1]) / (max[1] - min[1]) * @wnd_height
@draw()
@x = prevX
@y = prevY
exports.Point2D = Point2D
Point2D = require('../src/point2d').Point2D
describe "Point2D", ->
p1 = undefined
p2 = undefined
p3 = undefined
p4 = undefined
p5 = undefined
beforeEach ->
p1 = new Point2D(0, 0, 0, 0, 0, "", 0, 0, undefined)
p2 = new Point2D(100, 200, 30, 2.0, 2.0, "test", 640, 480, undefined)
p3 = new Point2D(100, 200, 30, 2.0, 2.0, "test", 640, 480, undefined)
p4 = new Point2D(1, 2, 30, 2.0, 2.0, "test", 640, 480, undefined)
p5 = new Point2D(2, 4, 30, 2.0, 2.0, "test", 640, 480, undefined)
it "should be x and y axis value are 0", ->
expect(p1.x).toEqual 0
expect(p1.y).toEqual 0
expect(p1.r).toEqual 0
expect(p1.vx).toEqual 0
expect(p1.vy).toEqual 0
expect(p1.wnd_width).toEqual 0
expect(p1.wnd_height).toEqual 0
expect(p1.label).toEqual ""
it "should be x and y axis value are 5, 10", ->
expect(p2.x).toEqual 100
expect(p2.y).toEqual 200
expect(p2.r).toEqual 30
expect(p2.vx).toEqual 2.0
expect(p2.vy).toEqual 2.0
expect(p2.wnd_width).toEqual 640
expect(p2.wnd_height).toEqual 480
expect(p2.label).toEqual "test"
it "should add velocity for x and y to 102.0 and 202.0", ->
p2.update_position()
expect(p2.x).toEqual 102.0
expect(p2.y).toEqual 202.0
it "should get x and y value with array style", ->
data = p3.get_data()
expect(data[0]).toEqual 100.0
expect(data[1]).toEqual 200.0
it "should get 5 as distance", ->
expect(p4.calc_distance(p5)).toEqual 5
Multi-Dimension Data
class DataPoint
constructor: (data, label, column_name) ->
@data = data
@label = label
@column_name = column_name
#
# Calculate distance between two points
# @param dp : DataPoint's instance
#
calc_distance: (dp) ->
sum = 0.0
for i in [0..@data.length-1]
sum += Math.pow(@data[i] - dp.data[i],2)
return Math.sqrt(sum)
get_data: ->
return @data
draw: ->
return
draw_with_scaling: ->
return
exports.DataPoint = DataPoint
DataPoint = require('../src/datapoint').DataPoint
describe "DataPoint", ->
p1 = undefined
p2 = undefined
p3 = undefined
p4 = undefined
beforeEach ->
column_name = ["a","b","c","d","e"]
p1 = new DataPoint([1, 2, 3, 4, 5], "test", column_name)
p2 = new DataPoint([5, 4, 3, 2, 1], "test", column_name)
p3 = new DataPoint([1, 3, 5, 7, 9], "test", column_name)
p4 = new DataPoint([3, 5, 2, 4, 6], "test", column_name)
it "should be x and y axis value are 5, 10", ->
expect(p1.data[0]).toEqual 1
expect(p1.data[1]).toEqual 2
expect(p1.data[2]).toEqual 3
expect(p1.data[3]).toEqual 4
expect(p1.data[4]).toEqual 5
expect(p1.label).toEqual "test"
expect(p1.column_name[0]).toEqual "a"
expect(p1.column_name[1]).toEqual "b"
expect(p1.column_name[2]).toEqual "c"
expect(p1.column_name[3]).toEqual "d"
expect(p1.column_name[4]).toEqual "e"
it "should get x and y value with array style", ->
data = p2.get_data()
expect(data[0]).toEqual 5
expect(data[1]).toEqual 4
expect(data[2]).toEqual 3
expect(data[3]).toEqual 2
expect(data[4]).toEqual 1
it "should get sqrt(35) as distance", ->
expect(p3.calc_distance(p4)).toEqual Math.sqrt(35)
Point Cloud Model
class PointCloud
constructor: (points)->
@points = points
@distances = []
for i in [0..points.length-1]
@distances.push([])
for j in [0..points.length-1]
@distances[i].push(0.0)
#
# Calc distances between points and make distance matrix
#
calc_distance: ->
for i in [0..@points.length-1]
for j in [0..@points.length-1]
@distances[i][j] = @points[i].calc_distance(@points[j])
#
# Draw points
#
draw_points: ->
for i in [0..@points.length-1]
@points[i].draw()
#
# Draw points with scaling
#
draw_points_with_scaling: ->
max = @points[0].get_data()
min = @points[0].get_data()
for i in [0..@points.length-1]
for j in [0..max.length-1]
if max[j] > @points[i].get_data()[j]
max[j] = @points[i].get_data()[j]
if min[j] < @points[i].get_data()[j]
min[j] = @points[i].get_data()[j]
for i in [0..@points.length-1]
@points[i].draw_with_scaling(max, min)
exports.PointCloud = PointCloud
PointCloud = require('../src/pointcloud').PointCloud
Point2D = require('../src/point2d').Point2D
describe "PointCloud", ->
pc = undefined
points = []
beforeEach ->
p1 = new Point2D(1, 2, 30, 2.0, 2.0, "test", 640, 480, undefined)
p2 = new Point2D(2, 4, 30, 2.0, 2.0, "test", 640, 480, undefined)
p3 = new Point2D(5, 3, 30, 2.0, 2.0, "test", 640, 480, undefined)
points.push(p1)
points.push(p2)
points.push(p3)
pc = new PointCloud(points)
it "should calculate distances", ->
pc.calc_distance()
expect(pc.distances[0][0]).toEqual 0
expect(pc.distances[1][1]).toEqual 0
expect(pc.distances[2][2]).toEqual 0
expect(pc.distances[0][1]).toEqual 5
expect(pc.distances[0][2]).toEqual 17
expect(pc.distances[1][0]).toEqual 5
expect(pc.distances[1][2]).toEqual 10
expect(pc.distances[2][0]).toEqual 17
expect(pc.distances[2][1]).toEqual 10
main
Point2D = require('../src/point2d').Point2D
DataPoint = require('../src/datapoint').DataPoint
PointCloud = require('../src/pointcloud').PointCloud
main = ()->
# Draw area
canvas = document.getElementById('canvas')
# Canvas interface
context = canvas.getContext('2d')
# Make blog name array
keys = []
for key of blog_data
keys.push(key)
num_points = keys.length
# Make data point's cloud
data_points = []
for i in [0..num_points-1]
data_points.push(new DataPoint(blog_data[keys[i]], keys[i], column_name))
realpc = new PointCloud(data_points)
# Make 2d(for canvas) point's cloud
points = []
for i in [0..num_points-1]
x = canvas.width /2 + Math.floor(Math.random() * 200) - 100
y = canvas.height /2 + Math.floor(Math.random() * 200) - 100
vx = 0.0
vy = 0.0
points.push new Point2D(x, y, 2, vx, vy, keys[i], canvas.width, canvas.height, context)
fakepc = new PointCloud(points)
fakepc.draw_points_with_scaling()
# Calculate realdist
realpc.calc_distance()
# Calculate initial fakedist
fakepc.calc_distance()
# Error tmp
lasterror = 0.0
# Learning Rate
rate = 0.0001
# Done flg of calculation
endflg = false
mainloop = ()->
context.save()
context.beginPath()
context.clearRect(0, 0, canvas.width, canvas.height)
context.restore()
if endflg
fakepc.draw_points_with_scaling()
else
totalerror = 0
# Calculate initial fakedist
fakepc.calc_distance()
# Calculate velocity of canvas point
for i in [0..num_points-1]
for j in [0..num_points-1]
if i==j
continue
errorterm = (fakepc.distances[j][i] - realpc.distances[j][i])/realpc.distances[j][i]
points[i].vx += ((points[i].x - points[j].x)/fakepc.distances[j][i])*errorterm
points[i].vy += ((points[i].y - points[j].y)/fakepc.distances[j][i])*errorterm
totalerror += Math.abs(errorterm)
# Check local minimum
console.log(totalerror)
if lasterror > 1.0 and lasterror < totalerror
endflg = true
else
# Save totalerror
lasterror = totalerror
# Update canvas point's position
for i in [0..num_points-1]
points[i].x -= rate * points[i].vx
points[i].y -= rate * points[i].vy
# Draw points
fakepc.draw_points_with_scaling()
# Do again after 30 millisecond
setTimeout(mainloop, 30)
mainloop()
window.onload = main
Result
