在R中绘制无重叠分类点凸包的技术问题求助
多类点无重叠凸包实现与凹包适用性分析
解决多类凸包重叠的思路与R实现
直接为每类点生成凸包会重叠,因为凸包仅以自身点集的最小包围为目标,完全不考虑其他类的位置。要实现无重叠的类级包围形状,核心是先为每类点划定专属空间区域,再在区域内生成包围形状。
基于Voronoi图的无重叠凸包实现
Voronoi图会将平面划分为多个单元,每个单元内的点到该类核心点的距离最近,天然保证单元间无重叠。我们可以在每个类对应的Voronoi单元内生成凸包,确保最终结果无重叠:
# 加载依赖包 library(sf) library(dplyr) # 模拟多类点数据(替换为你的实际数据) set.seed(123) points <- data.frame( x = c(rnorm(30, 0, 1), rnorm(30, 3, 1), rnorm(30, 6, 1)), y = c(rnorm(30, 0, 1), rnorm(30, 3, 1), rnorm(30, 6, 1)), class = rep(c("red", "blue", "green"), each = 30) ) %>% st_as_sf(coords = c("x", "y")) # 生成全局Voronoi图并按类聚合单元 voronoi_units <- st_voronoi(st_union(points)) %>% st_cast() %>% st_sf() %>% st_join(points, join = st_intersects) %>% group_by(class) %>% summarise(geometry = st_union(geometry)) # 在每个Voronoi单元内生成对应类的凸包 non_overlap_convex_hulls <- points %>% group_by(class) %>% summarise(class_points = st_union(geometry)) %>% left_join(voronoi_units, by = "class") %>% mutate( # 取类点与对应Voronoi单元的交集,再生成凸包 geometry = st_intersection(class_points, geometry) %>% st_convex_hull() ) %>% select(class, geometry)
基于类间距离的凸包收缩法
如果不想用Voronoi图,也可以计算每类点到其他所有类点的最小距离d,然后将该类的凸包向内收缩d/2的距离,确保两个凸包边界间至少有0的间隔:
# 计算每类到其他类的最小距离 class_distances <- points %>% st_distance(points) %>% as.matrix() %>% split(rep(seq_len(n_distinct(points$class)), each = nrow(points))) %>% lapply(function(x) apply(x[, points$class != unique(points$class)], 2, min)) %>% sapply(min) # 生成原始凸包并按距离收缩 non_overlap_convex_hulls <- points %>% group_by(class) %>% summarise(geometry = st_convex_hull(st_union(geometry))) %>% mutate( shrink_dist = class_distances[class] / 2, # 向内收缩凸包,避免重叠 geometry = st_buffer(geometry, dist = -shrink_dist) ) %>% select(class, geometry)
注意:如果类点本身分布非常密集,收缩后可能导致凸包消失,此时优先选择Voronoi方法。
凹包(Concave Hull)的适用性分析
凹包相比凸包更贴合点集的实际分布,能减少凸包带来的“无效包围空间”,因此在类点分布不交错的场景下,直接生成凹包确实比凸包更不容易出现重叠。但如果类点本身在空间上相互穿插,仅靠凹包依然无法避免重叠。
实际使用中,建议结合前面的区域约束方法:先通过Voronoi或距离划分专属区域,再在区域内生成凹包,这样既能贴合点集形状,又能严格保证无重叠。R中可以用concaveman包生成凹包:
library(concaveman) # 在Voronoi单元内生成无重叠凹包 non_overlap_concave_hulls <- points %>% group_by(class) %>% mutate(coords = st_coordinates(geometry)) %>% summarise(coords = list(coords)) %>% left_join(voronoi_units, by = "class") %>% mutate( # 生成凹包,concavity参数控制凹陷程度(值越大越接近凸包) concave_hull = lapply(coords, concaveman, concavity = 2), geometry = st_sfc(lapply(concave_hull, function(x) st_polygon(list(x)))) %>% st_intersection(geometry) ) %>% select(class, geometry)
总结
- 直接生成多类凸包必然重叠,因为凸包不考虑类间空间关系;
- 无重叠包围的核心是为每类划定专属区域(Voronoi单元是最稳妥的方式),再在区域内生成凸包/凹包;
- 凹包更贴合点集,能减少空间浪费,但无法单独解决类点交错导致的重叠问题,需结合区域约束使用。
内容的提问来源于stack exchange,提问作者stats_noob
相关产品推荐
相关产品推荐

