这里的k为聚类数,其解决以1种属性值(例如距离值)进行的多点分类问题。
例如我们要实现对坐标系中多个点进行聚类分组。思路如下:
1,输入条件1:坐标系中的N个固定点集合(已知每个点的x、y坐标值)
2,输入条件2:坐标系中任意k个聚类核心点的位置(通常会依据经验定位初始的聚类核心点位置)
3,执行过程1:遍历所有固定点计算每个点与k各聚类核心点的距离,并将各点归类到与其距离最近的核心点分类中(采用欧氏距离)。
4,执行过程2:依次遍历k个聚类,调整各聚类中核心点的位置到聚类中心(求聚类中所有点平均值的方式)
5,执行过程3:再次执行过程1,直到聚类核心点的位置不再发生变化。
Js算法如下:
/**
* 按给定的分组点生成分组
* @param points 所有点
* @param zonePoints 当前分组点
*/
function kMeans(points, zonePoints) {
for (var i = 0; i < points.length; i++) {
var p = points[i];
var minDistance = -1, minTp = null;
for (var j = 0; j < zonePoints.length; j++) {
var tp = zonePoints[j];
var distance = Math.sqrt(Math.pow(p.x - tp.x, 2) + Math.pow(p.y - tp.y, 2));
if (minDistance === -1 || minDistance > distance) {//计算每个点到各区域核心点的最短距离
minDistance = distance;
minTp = tp;
}
}
p.tpId = minTp.id;
$("#p" + p.id).css('background-color', minTp.color);//设置点颜色
printLog('p[' + p.id + ']{x:' + p.x + ',y:' + p.y + '}所属类别:' + (p.tpId === undefined ? '未设定' : p.tpId));
}
}
/**
* 计算每个分组的核心点,并修改分组点坐标
* @param points 所有点
* @param zonePoints 当前分组点
*/
function calcCenter(points, zonePoints) {
var bestRegionCoreSize = 0;
for (var j = 0; j < zonePoints.length; j++) {
var tp = zonePoints[j];
//1,找到分组的所有点
var pSumX = 0, pSumY = 0, pSize = 0;
for (var i = 0; i < points.length; i++) {
var p = points[i];
if (tp.id === p.tpId) {
// tpGroup.push(p);
pSumX += p.x;
pSumY += p.y;
pSize++;
}
}
//2,求组核心点
if (pSize > 0) {
var pCenterX = pSumX / pSize;
var pCenterY = pSumY / pSize;
if (tp.x === pCenterX && tp.y === pCenterY) {
bestRegionCoreSize++;
printLog('<font color="'+tp.color+'">找到一个最佳聚类核心,坐标是:{x:'+pCenterX+',y:'+pCenterY+'}</font>');
} else {
tp.x = pCenterX;
tp.y = pCenterY;
$("#tp" + tp.id).css({'left': pCenterX + 'px', 'top': pCenterY + 'px'});
}
}
}
if (bestRegionCoreSize === zonePoints.length) {
alert("已找到所有聚类的最佳核心点,所有点的聚类已完成");
return true;
} else {
return false;
}
}
演示demo下载地址:https://gitee.com/inq/k-means.git