曼哈顿度量中的 Voronoi 图

Voronoi diagram in Manhattan metric

我正在使用 scipy.spatial 来可视化 Voronoi 图。但是,此处使用的距离度量是欧几里德 (L2)。我正在我的 Voronoi 图上寻找曼哈顿 (L1) 度量的方法。有没有一种简单(或多或少)的方法来做到这一点?

import numpy as np
import matplotlib.pyplot as plt

points = np.array([[1.5, 1.], [3.5, 1.], [5., 2.], [2.5, 3.], [3.5, 1.], [4., 4.]])
    
from scipy.spatial import Voronoi, voronoi_plot_2d
vor = Voronoi(points)

fig = plt.figure()
ax = fig.add_subplot('111')
ax.plot(points[:, 0], points[:, 1], 'o', color='k')
ax.set_xlim([-1, 9])
ax.set_ylim([-1, 9])
voronoi_plot_2d(vor, ax)

基本上我想得到类似的东西,但在 L1 指标中。

我找到了 scipy.spatial.distance.cityblock 可以处理感兴趣的指标,但不完全确定我该如何实现它才能正常工作?

如果区域的可视化和计算是您唯一的要求,您可以使用这个名为 mcvoronoi 的 pip 库,我们之前做过。这是基于蒙特卡洛抽样。我添加了一个选项来更改此答案的距离度量。更新版本(带有距离度量选项)尚未在 pip 上发布,但您可以使用 github master 分支。用法如下图:

  • 克隆当前目录中的 repository
  • 运行 python example.py

example.py 由以下基本行组成:

lat_lon_area, mean_percentage_error = voronoi_area(points,voronoi_plot_enabled=True, NUM_COLORS=5, metric='manhattan')

图片保存如下图:

你当然可以通过增加采样点的数量来让它们变得超级脆。还会生成显示面积计算误差的误差图。

您可能想要使用更多颜色,但如果您有足够多的区域,通常略多于 4 colors 就足够了。

我创建了一个 github 存储库,其中包含一个名为 voronoiz 的 Python 包,其中包含函数 voronoi_l1(用于计算 L1 Voronoi 图的多边形)和 voronoi_grid(用于计算 scipy.spatial.cdist 支持的任何距离度量的 Voronoi 图图像)。

这些实现使用蛮力,O(n²) 算法,因此如果您将它用于数百万个点,它可能不会很好地工作,但对于少量到中等数量的点,您可以使用它来制作漂亮的情节。

例如,一组 10 个点的 Voronoi 图的这些动画是用 voronoi_grid 结合 write_apng 来自 [=19] =]图书馆:

L1 指标:

Minkowksi 度量,p=2(即标准欧几里得度量):

闵可夫斯基度量,p=4:

这是生成动画的脚本:

import numpy as np
from voronoiz import voronoi_grid
from numpngw import write_apng


xmin = 0
xmax = 5
ymin = 0
ymax = 5

points = np.array([[0.00, 0.00],
                   [1.00, 4.51],
                   [1.20, 0.30],
                   [2.50, 2.60],
                   [2.40, 0.80],
                   [4.40, 3.30],
                   [1.95, 3.00],
                   [3.71, 1.90],
                   [4.50, 3.66],
                   [4.67, 0.21]])

gridsize = 299

for kwargs in [dict(metric='cityblock'),
               dict(metric='minkowski', p=2),
               dict(metric='minkowski', p=4)]:
    imgs = []
    for theta in np.linspace(0, 2*np.pi, 250, endpoint=False):
        # points[0] will travel about a circle.
        points[0] = 2.5 + 1.5*np.array([np.cos(theta), np.sin(theta)])
        img = voronoi_grid(points, xmin, xmax, ymin, ymax,
                           gridsize=(gridsize, gridsize),
                           **kwargs)
        img = (160//(len(points)+1)*img + 64).astype(np.uint8)
        img[img == 64] = 0
        for x, y in points:
            i = int(gridsize*(x - xmin)/(xmax - xmin))
            j = int(gridsize*(y - ymin)/(ymax - ymin))
            img[j-1:j+2, i-1:i+2] = 255
        img = np.pad(img, pad_width=1, mode='constant', constant_values=255)
        imgs.append(img)

    tag = '_'.join(f"{key}_{value}" for key, value in kwargs.items())
    write_apng(f'animation_{tag}.png', imgs, delay=100)