You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python散点图中绘制包裹所有点的紧密填充区域?

绘制非凸点集的紧密填充边界

问题分析

scipy.spatial.ConvexHull只能生成包裹点集的最小凸多边形边界,无法捕捉你数据中存在的非凸曲线轮廓。要生成贴合所有点的紧密非凸边界,推荐使用Alpha Shape算法,或分上下边界拟合的方法。


方法一:Alpha Shape算法(推荐)

Alpha Shape通过Delaunay三角剖分筛选符合参数的三角形边,能生成贴合非凸点集的边界。

1. 读取数据

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay

# 读取数据(根据你的文件格式调整,示例为CSV)
df = pd.read_csv("你的数据文件.csv")
yloc = df['Points:1'].values
zloc = df['Points:2'].values
points = np.column_stack((yloc, zloc))

2. 实现Alpha Shape工具函数

def alpha_shape(points, alpha, only_outer=True):
    assert points.shape[0] > 3, "点集数量必须大于3"
    tri = Delaunay(points)
    edges = set()

    # 遍历所有三角形,筛选符合alpha条件的边
    for ia, ib, ic in tri.simplices:
        pa, pb, pc = points[ia], points[ib], points[ic]
        # 计算三角形外接圆半径
        a, b, c = np.linalg.norm(pa-pb), np.linalg.norm(pb-pc), np.linalg.norm(pc-pa)
        s = (a + b + c) / 2.0
        area = np.sqrt(s * (s-a) * (s-b) * (s-c)) if s*(s-a)*(s-b)*(s-c) > 0 else 0
        circum_r = a*b*c/(4.0*area) if area > 1e-10 else float('inf')
        
        if circum_r <= 1.0/alpha:
            edges.add(tuple(sorted((ia, ib))))
            edges.add(tuple(sorted((ib, ic))))
            edges.add(tuple(sorted((ic, ia))))

    # 构建连续的边界路径
    edge_list = list(edges)
    if not edge_list:
        return []
    
    current_edge = edge_list.pop(0)
    boundary = [current_edge[0], current_edge[1]]
    
    while edge_list:
        next_idx = None
        for i, e in enumerate(edge_list):
            if e[0] == boundary[-1]:
                next_idx = e[1]
                edge_list.pop(i)
                break
            elif e[1] == boundary[-1]:
                next_idx = e[0]
                edge_list.pop(i)
                break
        if next_idx is None:
            break
        boundary.append(next_idx)
    
    # 闭合边界
    if boundary[0] != boundary[-1]:
        boundary.append(boundary[0])
    
    # 过滤内部小环(可选)
    if only_outer:
        def poly_area(vertices):
            area = 0.0
            n = len(vertices)
            for i in range(n):
                x1, y1 = vertices[i]
                x2, y2 = vertices[(i+1)%n]
                area += (x1*y2) - (x2*y1)
            return abs(area)/2.0
        
        boundary_points = points[boundary]
        if poly_area(boundary_points) < 0.1:  # 阈值根据数据调整
            return []
    
    return boundary

3. 绘制填充边界

调整alpha参数控制边界紧密程度(值越小越贴合点集):

plt.scatter(yloc, zloc, s=5, label='原始点集')

# 计算并绘制Alpha Shape边界
alpha = 0.05  # 可根据实际效果调整,比如0.01~0.1之间尝试
boundary_idx = alpha_shape(points, alpha)
if boundary_idx:
    boundary_points = points[boundary_idx]
    plt.fill(boundary_points[:,0], boundary_points[:,1], color='red', alpha=0.2, label='紧密边界')
    plt.plot(boundary_points[:,0], boundary_points[:,1], 'r-', linewidth=1.5)

plt.xlabel('Y坐标')
plt.ylabel('Z坐标')
plt.legend()
plt.show()

方法二:分上下边界拟合

如果点集可按Y坐标排序,可分别提取上下边界并平滑拟合,再填充区域:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import splrep, splev

# 读取数据
df = pd.read_csv("你的数据文件.csv")
yloc = df['Points:1'].values
zloc = df['Points:2'].values

# 按Y坐标排序
sorted_idx = np.argsort(yloc)
sorted_y = yloc[sorted_idx]
sorted_z = zloc[sorted_idx]

# 滚动窗口提取上下边界极值
window_size = 20
upper_z = []
lower_z = []
for i in range(len(sorted_y)):
    start = max(0, i - window_size//2)
    end = min(len(sorted_y), i + window_size//2)
    upper_z.append(np.max(sorted_z[start:end]))
    lower_z.append(np.min(sorted_z[start:end]))

# 平滑曲线
upper_tck = splrep(sorted_y, upper_z, s=100)  # s值控制平滑程度
lower_tck = splrep(sorted_y, lower_z, s=100)
smooth_y = np.linspace(sorted_y.min(), sorted_y.max(), 500)
smooth_upper = splev(smooth_y, upper_tck)
smooth_lower = splev(smooth_y, lower_tck)

# 绘制
plt.scatter(yloc, zloc, s=5, label='原始点集')
plt.fill_between(smooth_y, smooth_lower, smooth_upper, color='red', alpha=0.2, label='紧密边界')
plt.plot(smooth_y, smooth_upper, 'r-', linewidth=1.5)
plt.plot(smooth_y, smooth_lower, 'r-', linewidth=1.5)

plt.xlabel('Y坐标')
plt.ylabel('Z坐标')
plt.legend()
plt.show()

内容的提问来源于stack exchange,提问作者Atharva Sunil Sathe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 19:53:16