聚类与统计
Turf 提供两种聚类:按空间密度自动分组的 DBSCAN,和指定组数的 KMeans。
聚类结果只是给每个点加了一个 cluster 属性,
后续统计要靠 clusterEach、collect、tag 这些配套函数。
两种聚类
| 对比 | clustersDbscan | clustersKmeans |
|---|---|---|
| 组数 | 由数据决定 | 调用时指定 |
| 核心参数 | maxDistance(邻域半径) | numberOfClusters(组数) |
| 噪声点 | 有,cluster 为空 | 无,每个点都归入某一组 |
| 结果稳定性 | 同一份数据结果固定 | 初始中心随机,结果可能每次不同 |
| 适用 | 找自然聚集的热点 | 把点位均衡分成 N 个片区 |
DBSCAN:按密度分组
maxDistance 是邻域半径,单位由 units 指定;
minPoints 是成为核心点所需的最少邻居数。两个参数决定「密」的标准。
var clustered = turf.clustersDbscan(points, 1.5, {
units: 'kilometers',
minPoints: 3,
mutate: true // 直接改原点集,不额外复制
});
var noise = clustered.features.filter(function (p) {
return p.properties.cluster === undefined;
});
console.log('组数', new Set(clustered.features
.map(function (p) { return p.properties.cluster; })
.filter(function (c) { return c !== undefined; })).size);
console.log('噪声点', noise.length);
DBSCAN 点有三种状态,写在 properties.dbscan 里:
core(核心点)、edge(边界点)、
noise(噪声,未归入任何组)。
噪声点不是错误,是数据本身没有聚集。
逐簇统计:clusterEach
拿到 cluster 属性后,用 clusterEach 遍历每一组,
比手动 groupBy 少写一层循环,也自带组号。
var groups = [];
turf.clusterEach(clustered, 'cluster', function (cluster, features) {
var fc = turf.featureCollection(features);
var box = turf.bboxPolygon(turf.bbox(fc));
var mid = turf.center(fc);
var sum = features.reduce(function (s, f) {
return s + (f.properties.value || 0);
}, 0);
groups.push({
cluster: cluster,
count: features.length,
total: sum,
center: turf.getCoord(mid),
bbox: turf.bbox(fc)
});
});
console.log(groups);
collect 与 tag:把统计写回要素
collect 把同一组的某个属性值收集成数组,
tag 再按条件把标签写到点上。两者常与聚类搭配,用于生成「组内均值」。
var withValues = turf.collect(clustered, 'cluster', 'value', 'values');
// 计算每组的平均值,写回每个点
turf.clusterEach(withValues, 'cluster', function (cluster, features) {
var vals = features[0].properties.values;
var avg = vals.reduce(function (a, b) { return a + b; }, 0) / vals.length;
features.forEach(function (f) {
f.properties.groupAvg = Number(avg.toFixed(2));
});
});
collect 只在同组要素之间收集;组号相同的点会被归到同一个数组里。
噪声点没有组号,不会被收集,需要单独处理。
KMeans:均衡分组
指定组数后,每个点都会得到一个组号。结果依赖初始中心, 同一份数据多次运行可能得到不同的分组边界。
var k = turf.clustersKmeans(points, {
numberOfClusters: 5,
mutate: true
});
var hulls = [];
turf.clusterEach(k, 'cluster', function (cluster, features) {
var hull = turf.convex(turf.featureCollection(features));
hull.properties = { cluster: cluster, size: features.length };
hulls.push(hull);
});
var regions = turf.featureCollection(hulls);
console.log(regions.features.length); // 5
注意事项
maxDistance 的单位
默认单位是公里。城市尺度的数据用 0.5 到 2 公里比较合理, 把米数直接写进去(例如 500)会让所有点连成一组。
mutate 改的是入参本身
mutate: true 省掉一次深拷贝,但也意味着原始点集被改写。
同一份数据既要聚类又要保留原样时,先 turf.clone(points) 再传。
凸包不能代表真实范围
KMeans 分组后用 convex 画范围,容易把远处的孤立点包进来,
形成一大片其实没有点的区域。范围展示应改用缓冲区的并集,
或直接画聚合圆。
经纬志(2026)。《聚类与统计》。https://www.jingweizhi.com/turf/tutorial/clustering.html(访问日期 2026-11-03)。