[英]K-mean Clustering R-Tree boost
存在具有不同屬性和輸入的各種聚類算法。 選擇算法之前需要考慮的是您要實現的目標。 您在問題中提及的k均值旨在將點集划分為k個簇。 因此,輸入是所需的群集數量。 另一方面,在您鏈接的博客中描述的算法是貪婪聚類算法的一種變體,旨在將點集划分為一定大小的圓形聚類。 輸入是所需簇的半徑。
執行不同數據和應用程序的k-means聚類算法有很多種,例如使用超平面分離2個n維子集或使用Voronoi圖(勞埃德算法)聚類(通常稱為k-means算法)。 @ Anony-Mousse在您的問題下的評論中還提到了基於密度的聚類算法。
在本文中,您提到它是貪婪聚類的分層版本。 他們必須計算多個縮放級別的聚類,並避免每次使用先前分析級別的聚類質心作為下一級別聚類的點源時都分析所有點。 但是,在此答案中,我將展示如何僅在一個級別上實現此算法。 因此,輸入將是一組點,並且將簇的大小作為半徑。 如果需要分層版本,則應計算輸出聚類的質心,並將它們用作下一級算法的輸入。
使用Boost.Geometry R樹,可以像這樣(在C ++ 11中)實現一級(因此不是分層)的算法:
#include <boost/geometry.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/range/adaptor/indexed.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <iostream>
#include <vector>
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
typedef bg::model::point<double, 2, bg::cs::cartesian> point_t;
typedef bg::model::box<point_t> box_t;
typedef std::vector<point_t> cluster_t;
// used in the rtree constructor with Boost.Range adaptors
// to generate std::pair<point_t, std::size_t> from point_t on the fly
template <typename First, typename Second>
struct pair_generator
{
typedef std::pair<First, Second> result_type;
template<typename T>
inline result_type operator()(T const& v) const
{
return result_type(v.value(), v.index());
}
};
// used to hold point-related information during clustering
struct point_data
{
point_data() : used(false) {}
bool used;
};
// find clusters of points using cluster radius r
void find_clusters(std::vector<point_t> const& points,
double r,
std::vector<cluster_t> & clusters)
{
typedef std::pair<point_t, std::size_t> value_t;
typedef pair_generator<point_t, std::size_t> value_generator;
if (r < 0.0)
return; // or return error
// create rtree holding std::pair<point_t, std::size_t>
// from container of points of type point_t
bgi::rtree<value_t, bgi::rstar<4> >
rtree(points | boost::adaptors::indexed()
| boost::adaptors::transformed(value_generator()));
// create container holding point states
std::vector<point_data> points_data(rtree.size());
// for all pairs contained in the rtree
for(auto const& v : rtree)
{
// ignore points that were used before
if (points_data[v.second].used)
continue;
// current point
point_t const& p = v.first;
double x = bg::get<0>(p);
double y = bg::get<1>(p);
// find all points in circle of radius r around current point
std::vector<value_t> res;
rtree.query(
// return points that are in a box enclosing the circle
bgi::intersects(box_t{{x-r, y-r},{x+r, y+r}})
// and were not used before
// and are indeed in the circle
&& bgi::satisfies([&](value_t const& v){
return points_data[v.second].used == false
&& bg::distance(p, v.first) <= r;
}),
std::back_inserter(res));
// create new cluster
clusters.push_back(cluster_t());
// add points to this cluster and mark them as used
for(auto const& v : res) {
clusters.back().push_back(v.first);
points_data[v.second].used = true;
}
}
}
int main()
{
std::vector<point_t> points;
for (double x = 0.0 ; x < 10.0 ; x += 1.0)
for (double y = 0.0 ; y < 10.0 ; y += 1.0)
points.push_back(point_t{x, y});
std::vector<cluster_t> clusters;
find_clusters(points, 3.0, clusters);
for(size_t i = 0 ; i < clusters.size() ; ++i) {
std::cout << "Cluster " << i << std::endl;
for (auto const& p : clusters[i]) {
std::cout << bg::wkt(p) << std::endl;
}
}
}
另請參見其實現: https : //github.com/mapbox/supercluster/blob/master/index.js#L216
此外,請考慮@ Anony-Mousse關於全球距離計算准確性的評論。 上面的解決方案是針對笛卡爾坐標系的。 如果要使用不同的坐標系,則必須定義不同的點類型,例如,使用bg::cs::spherical_equatorial<bg::degree>
或bg::cs::geographic<bg::degree>
代替bg::cs::cartesian
您還必須以其他方式生成查詢邊界框。 但是bg::distance()
會在更改點類型后自動返回正確的距離。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.