16#ifndef izzi_POINTS_CLUSTER_H
17#define izzi_POINTS_CLUSTER_H 1
21#include <unordered_map>
59 if (
points.empty())
return {};
62 std::unordered_map<std::pair<int, int>,
vpoints,
63 decltype([](
const auto& p) {
64 return std::hash<int>{}(p.first) ^ (std::hash<int>{}(p.second) << 1);
67 for (
const auto& point :
points)
69 int grid_x =
static_cast<int>(point.x /
radius);
70 int grid_y =
static_cast<int>(point.y /
radius);
71 grid[{grid_x, grid_y}].push_back(point);
75 for (
const auto& [cell, cell_points] : grid)
77 if (!cell_points.empty())
80 result.emplace_back(centroid, cell_points.size());
91 if (
points.empty())
return {};
93 std::vector<vpoints> clusters;
94 std::vector<bool> assigned(
points.size(),
false);
96 for (
size_t i = 0; i <
points.size(); ++i) {
97 if (assigned[i])
continue;
100 std::queue<size_t> to_process;
104 while (!to_process.empty()) {
105 size_t current_idx = to_process.front();
107 current_cluster.push_back(
points[current_idx]);
110 for (
size_t j = 0; j <
points.size(); ++j) {
118 if (!current_cluster.empty()) {
119 clusters.push_back(std::move(current_cluster));
130 if (
points.empty())
return {};
133 std::vector<vpoints> clusters(centroids.size());
135 for (
size_t iter = 0; iter < max_iterations; ++iter)
138 for (
const auto& point :
points)
140 double min_dist = std::numeric_limits<double>::max();
141 int best_cluster = -1;
143 for (
size_t i = 0; i < centroids.size(); ++i)
145 double dist = point.distance(centroids[i]);
146 if (dist <=
radius && dist < min_dist)
149 best_cluster =
static_cast<int>(i);
153 if (best_cluster != -1)
154 clusters[best_cluster].push_back(point);
159 for (
size_t i = 0; i < clusters.size(); ++i)
161 if (!clusters[i].empty())
166 if (new_centroids.size() == centroids.size())
168 bool converged =
true;
169 for (
size_t i = 0; i < centroids.size(); ++i)
171 if (centroids[i].distance(new_centroids[i]) > 1e-6)
181 centroids = std::move(new_centroids);
182 clusters.resize(centroids.size());
192 if (
points.empty())
return {};
196 std::vector<voronoi_cell> cells;
198 for (
const auto& site : sites) {
199 cells.emplace_back(site);
202 for (
size_t iter = 0; iter < max_iterations; ++iter) {
204 for (
auto& cell : cells) {
209 for (
const auto& point :
points) {
210 double min_dist = std::numeric_limits<double>::max();
211 size_t best_cell = 0;
213 for (
size_t i = 0; i < cells.size(); ++i) {
214 double dist = point.distance(cells[i].site);
215 if (dist < min_dist) {
223 cells[best_cell].points.push_back(point);
228 bool converged =
true;
229 for (
size_t i = 0; i < cells.size(); ++i) {
230 if (!cells[i].
points.empty()) {
232 if (cells[i].site.distance(new_site) > 1e-6) {
235 cells[i].site = new_site;
239 if (converged)
break;
242 cells.erase(std::remove_if(cells.begin(), cells.end(),
243 [](
const voronoi_cell& cell) { return cell.points.empty(); }),
261 std::vector<bool> selected(
points.size(),
false);
264 std::random_device rd;
265 std::mt19937 gen(rd());
266 std::uniform_int_distribution<size_t> dist(0,
points.size() - 1);
268 size_t first_idx = dist(gen);
269 centroids.push_back(
points[first_idx]);
270 selected[first_idx] =
true;
273 for (
size_t i = 0; i <
points.size(); ++i)
278 bool far_enough =
true;
279 for (
const auto& centroid : centroids)
290 centroids.push_back(
points[i]);
302 if (!cluster.empty())
307 for (
const auto& point : cluster)
311 if (!point.name.empty())
314 cp =
Point(sum_x / cluster.size(), sum_y / cluster.size(), name);
323 for (
const auto& cluster : clusters) {
324 if (!cluster.empty()) {
326 result.emplace_back(centroid, cluster.size());
335 for (
size_t i = 0; i < cells.size(); ++i)
337 for (
size_t j = i + 1; j < cells.size(); )
339 if (cells[i].site.distance(cells[j].site) <
radius * 0.5)
342 auto& cpoints = cells[i].points;
343 cpoints.insert(cpoints.end(),
344 cells[j].points.begin(), cells[j].points.end());
348 cells.erase(cells.begin() + j);
360 for (
const auto& cell : cells)
362 if (!cell.points.empty())
366 for (
const auto& subcluster : subclusters)
368 if (!subcluster.empty())
371 result.emplace_back(centroid, subcluster.size());
383 std::vector<vpoints> subclusters;
385 if (cell.
points.empty())
return subclusters;
388 std::vector<bool> assigned(cell.
points.size(),
false);
390 for (
size_t i = 0; i < cell.
points.size(); ++i) {
391 if (assigned[i])
continue;
394 std::queue<size_t> to_process;
398 while (!to_process.empty()) {
399 size_t current_idx = to_process.front();
401 current_subcluster.push_back(cell.
points[current_idx]);
404 for (
size_t j = 0; j < cell.
points.size(); ++j) {
413 if (!current_subcluster.empty()) {
414 subclusters.push_back(std::move(current_subcluster));
425 const std::string& method)
429 if (method ==
"grid") {
431 }
else if (method ==
"hierarchical") {
433 }
else if (method ==
"kmeans") {
435 }
else if (method ==
"voronoi") {
438 throw std::invalid_argument(
"Unknown clustering method");
446 std::cout <<
"Clusters found: " << clusters.size() <<
"\n";
447 for (
size_t i = 0; i < clusters.size(); ++i)
449 const auto& wp = clusters[i];
450 std::cout <<
"Cluster " << i + 1 <<
": " << wp.pt.x <<
"," << wp.pt.y
451 <<
" weight: " << wp.weight <<
" name: " << wp.pt.name
456 {
return sum + wp.weight; };
458 std::cout <<
"Total points represented: "
459 << std::accumulate(clusters.begin(), clusters.end(), 0ULL, lklstr)
Cluster of points. Reduce a set of points via a given cluster algorithm.
vwpoints reduce_voronoi(size_t max_iterations=50)
Algorithm 4: Voronoi-Based Clustering with Radius Constraint.
vwpoints reduce_grid()
Algorithm 1: Grid-based Clustering (Simple and Fast)
vpoints initialize_centroids()
vwpoints weigh_cluster(const std::vector< vpoints > &clusters)
std::vector< vpoints > split_large_cell(const voronoi_cell &cell)
vwpoints constrain_voronoi_clusters(const std::vector< voronoi_cell > &cells)
vwpoints reduce_hierarchical_agglomerative()
Algorithm 2: Hierarchical Agglomerative Clustering (More Accurate)
Point calculate_centroid(const vpoints &cluster)
vwpoints reduce_constrained_k_means(size_t max_iterations=100)
Algorithm 3: K-Means Inspired with Distance Constraint.
void merge_close_sites(std::vector< voronoi_cell > &cells)
point_cluster(const vpoints &input_points, const double r)
vwpoints clusterPoints(const vpoints &points, double radius, const std::string &method)
void printClusters(const vwpoints &clusters)
std::vector< Point > vpoints
std::vector< WeightedPoint > vwpoints
Voronoi diagram related structures.
voronoi_cell(const Point &p)