代码之家  ›  专栏  ›  技术社区  ›  Behzad

K-均值聚类R-树boost

  •  0
  • Behzad  · 技术社区  · 6 年前

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

    1 回复  |  直到 5 年前
        1
  •  4
  •   Behzad    6 年前

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

    有各种算法执行k-means聚类,用于不同的数据和应用,如用超平面分离2个n维子集或用Voronoi图(劳埃德算法)聚类,通常称为k-means算法。在您的问题下方的评论中,还有@anonymousse提到的基于密度的聚类算法。

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

    使用Boost。几何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 Mouse关于全球距离计算准确性的评论。上述解决方案适用于笛卡尔坐标系。如果要使用不同的坐标系,则必须以不同的方式定义点类型,例如使用 bg::cs::spherical_equatorial<bg::degree> bg::cs::geographic<bg::degree> 而不是 bg::cs::cartesian . 您还必须以不同的方式生成查询边界框。但是 bg::distance() 更改点类型后将自动返回正确的距离。