c++ - K 均值聚类 R 树 boost

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

我正在使用R-Tree boost 。我在 r-tree boost 中添加了十万点。现在我想像这样对我的点进行聚类和分组 link 。看来我应该从点计算 k 均值。如何从 r 树点几何形状计算 k 均值。

最佳答案

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

有多种算法可以执行用于不同数据和应用的 k 均值聚类,例如使用超平面分离 2 个 n 维子集或使用 Voronoi 图(劳埃德算法)进行聚类(通常称为 k 均值算法)。 @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()改变点类型后会自动返回正确的距离。

关于c++ - K 均值聚类 R 树 boost ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49093958/

相关文章:

c++ - `sqlite3` 忽略 `sqlite3_busy_timeout` 吗?

c++ - 模板可能导致函数定义匹配失败

cluster-analysis - 在线 k 均值聚类

algorithm - K-means 能否用于帮助基于像素值的图像分离?

c++ - 不允许循环引用的 boost 图

python - K-means 颜色聚类 - 使用屏蔽 numpy 数组省略背景像素

c++ - -rdynamic 在静态二进制文件中使用 dlopen 时不起作用

c++ - 为什么 term 不评估一个带 0 个参数的函数?

c++ - 如何使用显式强制转换来抑制此警告?

C++ 程序中函数的 Python 绑定(bind)