如何将ConvexHull凸包平滑为类椭圆形状,在Matplotlib散点图中绘制包围区域
平滑包围区域实现方案
这里分两种常用实现方式,你可以根据需求选择:
方案1:基于凸包的B样条平滑
该方案在你原有凸包逻辑的基础上做插值平滑,不会大幅偏离原始凸包的覆盖范围,适合需要严格贴合点集外围边界的场景。
额外依赖导入
需要补充导入以下库:
from scipy.spatial import ConvexHull from scipy.interpolate import splprep, splev
改写后的encircle函数
def encircle(x, y, ax=None, smooth_level=0.5, sample_points=200, **kw): ax = ax or plt.gca() p = np.stack([x, y], axis=1) # 计算原始凸包 hull = ConvexHull(p) # 提取凸包顶点并补全闭合路径 hull_vertices = p[hull.vertices] hull_vertices = np.vstack([hull_vertices, hull_vertices[0]]) # B样条插值生成平滑路径,per=True标识为闭合曲线 tck, _ = splprep(hull_vertices.T, s=smooth_level, per=True) # 采样生成足够多的平滑点 u_new = np.linspace(0, 1, sample_points) x_smooth, y_smooth = splev(u_new, tck) # 生成平滑多边形并添加到画布 poly = patches.Polygon(np.c_[x_smooth, y_smooth], closed=True, **kw) ax.add_patch(poly)
参数说明:
smooth_level:平滑程度,数值越大平滑效果越强,数值越小越贴合原始凸包棱角,建议取值范围0.1~1sample_points:平滑后采样的点数量,数值越高曲线越顺滑
方案2:正态分布置信椭圆
该方案直接基于点集的均值和协方差拟合椭圆,形状规则平滑,适合数据近似正态分布的场景。
改写后的encircle函数
def encircle(x, y, ax=None, n_std=2.2, **kw): ax = ax or plt.gca() p = np.stack([x, y], axis=1) # 计算点集均值、协方差矩阵 mean = np.mean(p, axis=0) cov = np.cov(p, rowvar=False) # 计算特征值、特征向量确定椭圆长短轴方向 vals, vecs = np.linalg.eigh(cov) order = vals.argsort()[::-1] vals, vecs = vals[order], vecs[:, order] # 计算椭圆旋转角度 theta = np.degrees(np.arctan2(*vecs[:, 0][::-1])) # 计算长短轴长度,n_std为置信区间倍数,2对应95%数据覆盖范围 width, height = 2 * n_std * np.sqrt(vals) # 生成椭圆并添加到画布 ellipse = patches.Ellipse(xy=mean, width=width, height=height, angle=theta, **kw) ax.add_patch(ellipse)
参数说明:
n_std:控制椭圆覆盖范围,数值越大椭圆越大,建议设置为2~3保证所有目标点都在椭圆内
修改完encircle函数后直接运行你原有代码即可得到平滑的包围区域效果。

内容的提问来源于stack exchange,提问作者ivaquero
相关产品推荐
相关产品推荐

