K均值聚类R树提升

K-mean Clustering R-Tree boost

我正在使用 R-Tree boost. I added a hundred thousand points in r-tree boost. Now I want to cluster and group my points like this link。看来我应该从点计算 k 均值。如何从 r 树点几何计算 k 均值。

有多种具有不同属性和输入的聚类算法。选择算法之前需要考虑的是你要实现什么。您在问题中提到的 k-means 旨在将一组点划分为 k 个簇。所以输入是所需的簇数。另一方面,您链接的博客中描述的算法是贪婪聚类算法的一种变体,旨在将点集划分为一定大小的圆形聚类。输入是所需簇的半径。

有多种算法执行 k-means 聚类,用于不同的数据和应用程序,例如使用超平面分离 2 个 n 维子集或使用 Voronoi 图(Lloyd 算法)进行聚类,通常称为 k-means 算法。还有@Anony-Mousse在你问题下的评论里提到的基于密度的聚类算法

在文章中,您提到它是贪婪聚类的分层版本。他们必须计算多个缩放级别的聚类,并避免每次使用先前分析级别的聚类质心作为下一级聚类的点源时分析所有点。但是,在这个答案中,我将展示如何仅在一个级别上实现该算法。所以输入将是一组点和一个簇的大小作为半径。如果您需要分层版本,您应该计算输出簇的质心并将它们用作下一级算法的输入。

使用Boost.Geometry R-tree,一层(因此不是分层)的算法可以像这样实现(在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()会在更改点类型后自动return修正距离。