使用Scipy生成Voronoi图出现异常结果的问题排查
Voronoi图边界缺失/异常的解决思路
问题背景
使用Scipy的Voronoi工具生成图时,出现边界缺失、显示异常的问题,原始数据和代码如下:
原始DataFrame
import pandas as pd centers_dt = pd.DataFrame({ 'cl': {0: 'A', 1: 'C', 2: 'H', 3: 'M', 4: 'S'}, 'd': {0: 245.059986986012, 1: 320.49044143557785, 2: 239.79023081978914, 3: 263.38325791238833, 4: 219.53334398353175}, 'p': {0: 10.971011721360075, 1: 10.970258360366753, 2: 13.108487516946218, 3: 12.93241352743668, 4: 13.346107628161008} })
原始绘图代码
import numpy as np import matplotlib.pyplot as plt from scipy.spatial import Voronoi, voronoi_plot_2d centers2 = np.array(centers_dt[['d', 'p']]) scatter_x = np.array(centers_dt['d']) scatter_y = np.array(centers_dt['p']) group = np.array(centers_dt['cl']) cdict = {'C': 'red', 'A': 'blue', 'H': 'green', 'M': 'yellow', 'S': 'black'} fig, ax = plt.subplots() for g in np.unique(group): ix = np.where(group == g) ax.scatter(scatter_x[ix], scatter_y[ix], c = cdict[g], label = g, s = 100) ax.legend() vor = Voronoi(centers2) fig = voronoi_plot_2d(vor,plt.gca()) plt.show() plt.close()
核心原因
Scipy的Voronoi默认不会处理无限远顶点(即位于数据集边界之外的Voronoi区域顶点),导致这些区域的边界无法正常绘制;同时绘图时坐标轴范围未适配,会进一步裁剪掉部分边界。
解决步骤
1. 补充无限远顶点的边界坐标
手动计算数据的极值范围,将无限远顶点替换为落在坐标轴边界上的点,确保所有Voronoi区域都有闭合边界。
2. 统一坐标轴范围
设置坐标轴的上下限,覆盖所有数据点和补充的边界点,避免图形被裁剪。
3. 优化绘图逻辑
确保Voronoi图和散点图在同一轴上绘制,避免图层冲突。
修改后的完整代码
import numpy as np import matplotlib.pyplot as plt from scipy.spatial import Voronoi, voronoi_plot_2d import pandas as pd # 原始数据 centers_dt = pd.DataFrame({ 'cl': {0: 'A', 1: 'C', 2: 'H', 3: 'M', 4: 'S'}, 'd': {0: 245.059986986012, 1: 320.49044143557785, 2: 239.79023081978914, 3: 263.38325791238833, 4: 219.53334398353175}, 'p': {0: 10.971011721360075, 1: 10.970258360366753, 2: 13.108487516946218, 3: 12.93241352743668, 4: 13.346107628161008} }) centers2 = np.array(centers_dt[['d', 'p']]) scatter_x = centers_dt['d'].values scatter_y = centers_dt['p'].values group = centers_dt['cl'].values cdict = {'C': 'red', 'A': 'blue', 'H': 'green', 'M': 'yellow', 'S': 'black'} # 计算数据边界,留10%的余量 x_min, x_max = scatter_x.min() * 0.9, scatter_x.max() * 1.1 y_min, y_max = scatter_y.min() * 0.9, scatter_y.max() * 1.1 vor = Voronoi(centers2) # 初始化绘图,先绘制基础Voronoi线条(隐藏顶点) fig, ax = plt.subplots() voronoi_plot_2d(vor, ax=ax, show_vertices=False, line_colors='gray', line_width=2, line_alpha=0.6, point_size=0) # 绘制分类散点 for g in np.unique(group): ix = group == g ax.scatter(scatter_x[ix], scatter_y[ix], c=cdict[g], label=g, s=100) # 补充无限远区域的闭合边界 for region_idx, region in enumerate(vor.regions): # 跳过空区域和无无限远顶点的区域 if not region or -1 not in region: continue # 获取对应的数据点中心,判断边界延伸方向 point_idx = vor.point_region.index(region_idx) point_center = centers2[point_idx] vertices = [] for v_idx in region: if v_idx == -1: # 根据中心位置,添加坐标轴边界点 if point_center[0] < (x_min + x_max)/2: vertices.append([x_min, point_center[1]]) else: vertices.append([x_max, point_center[1]]) if point_center[1] < (y_min + y_max)/2: vertices.append([point_center[0], y_min]) else: vertices.append([point_center[0], y_max]) else: vertices.append(vor.vertices[v_idx]) # 绘制闭合多边形 polygon = plt.Polygon(vertices, fill=False, edgecolor='gray', linewidth=2, alpha=0.6) ax.add_patch(polygon) # 设置坐标轴范围和标签 ax.set_xlim(x_min, x_max) ax.set_ylim(y_min, y_max) ax.set_xlabel('d') ax.set_ylabel('p') ax.legend() plt.title('Voronoi Diagram with Complete Boundaries') plt.show() plt.close()
关键说明
- 处理无限远顶点时,通过对应数据点的中心位置判断边界延伸方向,确保Voronoi区域闭合。
- 给数据范围留10%的余量,避免图形紧贴坐标轴边缘。
- 按「Voronoi线条→散点→补充边界」的顺序绘图,确保图层显示正常。
内容的提问来源于stack exchange,提问作者quant
相关产品推荐
相关产品推荐

