K均值聚类R树增强

时间:2018-03-04 09:36:32

标签: c++ boost k-means boost-geometry r-tree

我正在使用R-Tree boost。我在r-tree boost中增加了十万点。现在我想集中点我的点link。似乎我应该从点计算k均值。如何从r树点几何计算k均值。

1 个答案:

答案 0 :(得分:0)

有各种聚类算法具有不同的属性和输入。在选择算法之前需要考虑的是您想要实现的目标。你在问题中提到的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()会在更改点类型后自动返回正确的距离。