...
 
Commits (6)
image: node
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
before_script:
- npm -q install
compile:
stage: build
script:
- npm run -q compile
- npm run -q compile:examples
check:
stage: test
script:
- npm run -q lint
pages:
stage: deploy
script:
- mkdir public
- npm run -q compile:examples
- cp example/dist/bundle.js public/
- cp example/public/index.html public/
- "echo -e \"DATE: $(date '+%Y-%m-%d %H:%M:%S')\\nREF: $(git rev-parse --verify HEAD)\" > public/dist.info.txt"
artifacts:
paths:
- public
only:
- master
import * as d3 from "d3";
import {Delaunay} from "d3-delaunay";
import {kmeans} from "../";
import {Cluster, kmeans} from "../";
const colors = ["blue", "green", "yellow", "cyan", "magenta", "brown", "grey"];
let data: any = {};
......@@ -14,6 +14,7 @@ window.onload = () => {
document.getElementById("clusterSeeds").onchange = render;
document.getElementById("voronoi").onchange = render;
document.getElementById("iteration").oninput = render;
loadForm();
};
......@@ -36,12 +37,23 @@ const seed = () => {
};
const clusterize = () => {
data.clusters = kmeans(data.points, data.clusterCount);
const iterations = [];
data.clusters = kmeans(data.points, data.clusterCount, {
onIteration: (c: Cluster[]) => {
iterations.push(c);
}
});
data.iterations = iterations;
data.iterationIdx = iterations.length - 1;
document.getElementById("iteration")["max"] = iterations.length - 1;
document.getElementById("iteration")["value"] = iterations.length - 1;
render();
};
const render = () => {
const {clusters, width, height, seeds} = data;
const iterationIdx = parseInt(document.getElementById("iteration")["value"]);
const {iterations, width, height, seeds} = data;
const clusters = iterations[iterationIdx];
document.getElementById("graph").innerHTML = null;
const svg = d3.select("#graph").append("svg").attr("width", width).attr("height", height);
......
......@@ -20,6 +20,7 @@
<label>Display Voronoi cells <input id="voronoi" value="1" type="checkbox"/></label><br/>
<button type="button" id="resetData">New point set</button><br/>
<button type="button" id="recluster">Cluster again</button>
<input type="range" min="0" max="5" step="1" id="iteration"/>
</form>
</div>
<div style="height: 100%;" id="graph"></div>
......
......@@ -28,10 +28,14 @@ const config = {
},
entry: {
// Set index.tsx as application entry point.
bundle: "./example/index.ts"
bundle: path.join(__dirname, "./index.ts")
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js"
},
devServer: {
contentBase: path.join(__dirname, "example")
contentBase: path.join(__dirname, "public")
},
plugins: [
new webpack.DefinePlugin({
......
import { merge } from "typescript-object-utils";
import { Cluster, optimizeCentroids } from "./src/kmeans";
import { Point } from "./src/point";
import { randomPick } from "./src/randomPick";
export { Cluster };
export function kmeans(points: Point[], k: number, options?: Partial<KMeansOptions>): Cluster[] {
options = merge(defaults, options);
const centroids = options.initFunction(points, k);
return optimizeCentroids(centroids, points);
}
export interface KMeansOptions {
initFunction: (points: Point[], k: number) => Point[];
}
const defaults: KMeansOptions = {
initFunction: randomPick
};
\ No newline at end of file
export { Cluster, kmeans, KMeansOptions } from "./src";
......@@ -13,12 +13,13 @@
],
"scripts": {
"compile": "tsc -d",
"compile:examples": "webpack --config example/webpack.config.ts",
"prepublishOnly": "npm run lint && tsc -d",
"lint": "tslint -t verbose index.ts src/* src/**/*",
"test": "mocha tests/*.test.ts tests/**/*.test.ts --opts ./mocha.opts",
"test:coverage": "nyc mocha tests/*.test.ts tests/**/*.test.ts --opts ./mocha.opts",
"test:coverage:html": "nyc --reporter=html mocha tests/*.test.ts tests/**/*.test.ts --opts ./mocha.opts && open .coverage/index.html",
"example": "webpack-dev-server --open"
"lint": "tslint -t verbose index.ts src/**.ts example/**.ts",
"test": "mocha tests/**.test.ts --opts ./mocha.opts",
"test:coverage": "nyc mocha tests/**.test.ts --opts ./mocha.opts",
"test:coverage:html": "nyc --reporter=html mocha tests/**.test.ts --opts ./mocha.opts && open .coverage/index.html",
"example": "webpack-dev-server --config example/webpack.config.ts --open"
},
"author": "Daniel Król",
"license": "MIT",
......
import { merge } from "typescript-object-utils";
import { Cluster, optimizeCentroids } from "./kmeans";
import { Point } from "./point";
import { randomPick } from "./randomPick";
export { Cluster };
export function kmeans(points: Point[], k: number, options?: Partial<KMeansOptions>): Cluster[] {
options = merge(defaults, options);
const centroids = options.initFunction(points, k);
return optimizeCentroids(centroids, points, options.onIteration);
}
export interface KMeansOptions {
initFunction: (points: Point[], k: number) => Point[];
onIteration?: (clusters: Cluster[]) => any;
}
const defaults: KMeansOptions = {
initFunction: randomPick
};
\ No newline at end of file
import {assembleClusters} from "./assembleClusters";
import { arePointsEqual, Point } from "./point";
export function optimizeCentroids<L>(centroids: Point[], points: Point[]): Cluster[] {
let clusters = assembleClusters(centroids, points);
let run = true;
while(run) {
run = false;
export function optimizeCentroids(centroids: Point[], points: Point[], onIteration?: (clusters: Cluster[]) => any): Cluster[] {
let clusters = [];
let anyCentroidChanged = false;
do {
clusters = assembleClusters(centroids, points);
if(onIteration) {
onIteration(clusters);
}
anyCentroidChanged = false;
centroids = [];
for(const cluster of clusters) {
const centroid = clusterCentroid(cluster);
centroids.push(centroid);
if(!arePointsEqual(centroid, cluster.centroid)) {
run = true;
anyCentroidChanged = true;
}
}
clusters = assembleClusters(centroids, points);
}
} while( anyCentroidChanged );
return clusters;
}
......
......@@ -24,4 +24,15 @@ describe("kMeans", () => {
return a.centroid[0] - b.centroid[0];
});
});
it("should call iteration listener", () => {
const points = [[0], [1], [9], [10]];
let iterations = 0;
const clusters = kmeans(points, 2, {
onIteration: () => {
iterations++;
}
});
assert.strictEqual(iterations > 0, true);
});
});