经纬志
首页 / Turf.js / 教程

聚类与统计

先看结论

Turf 提供两种聚类:按空间密度自动分组的 DBSCAN,和指定组数的 KMeans。 聚类结果只是给每个点加了一个 cluster 属性, 后续统计要靠 clusterEach、collect、tag 这些配套函数。

最后验证 2026-11-03 · Turf.js 7.x · 更新 2026-10-29 · 引用本文

两种聚类

对比clustersDbscanclustersKmeans
组数由数据决定调用时指定
核心参数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)。