在 networkx 图中的单个单元格中填充颜色

Fill color in single cells in a networkx graph

我用 networkx 构建了一个图,看起来像这样:Graph

我想用指定的颜色填充每个单元格。该图由 nx.draw_networkx_edges() (returns 一个 LineCollection)绘制。我在这里发现了一个类似的问题 (Fill area between lines),但是评论中的解决方案对我不起作用。

我还使用了 plt.fill_between 和更简单的图表并手动设置值:

plt.fill_between([1, 2], [2, 2], color='yellow')
plt.fill_between([1.75, 2, 3], [1.25, 2, 2], color='purple')
plt.fill_between([0, 1, 1.25], [2, 2, 1.25], color='red')
plt.fill_between([0.75, 1.25, 1.75, 2.25], [0.75, 1.25, 1.25, 0.75], color='blue')
plt.fill_between([2, 2.25, 3], [0, 0.75, 1], color='pink')
plt.fill_between([0, 0.75, 1], [1, 0.75, 0], color='green')

结果很不错 (result),但问题是,填充取决于单元格填充时的顺序,这会使算法变得复杂,我猜。

有谁知道更好更简单的解决方案吗?

编辑: 我试图将图形转换为 Voronoi 图以尝试@JohanC 的解决方案,但运行时间相当长,而且较大图形的解决方案并不准确。为了计算质心,我使用了这个 Center of Polygon

def find_Centroid(v):
    sum_A = 0
    sum_x = 0
    sum_y = 0
    for i in range(len(v)):
        next = i+1 if i != len(v)-1 else 0
        sum_A += v[i][0]*v[next][1] - v[next][0]*v[i][1]
        sum_x += (v[i][0] + v[next][0]) * (v[i][0]*v[next][1] - v[next][0]*v[i][1])
        sum_y += (v[i][1] + v[next][1]) * (v[i][0]*v[next][1] - v[next][0]*v[i][1])
    A = 1/2 * sum_A
    Cx = 1/(6*A) * sum_x
    Cy = 1/(6*A) * sum_y

    return Cx, Cy

# Get all cells of Graph (I think that takes most of the time)
cycle = nx.minimum_cycle_basis(SVG)

centroids = list()

# calculate all centroids of the cells
for c in cycle:
    subG = SVG.subgraph(c)
    sortedCycle = sortGraphNodes(subG)
    centroid = find_Centroid(sortedCycle)
    SVG.add_node((centroid[0], centroid[1]))
    centroids.append(centroid)

vor = Voronoi(centroids)
voronoi_plot_2d(vor)
plt.show()

Result small graph Result large graph

使用显示填充更简单图形的问题中的第一个代码块,我构建了一个示例网络。 下面列出了边缘:

edges = [((1, 2), (2, 2)),
         ((1, 2), (0, 2)),
         ((1, 2), (1.25, 1.25)),
         ((2, 2), (1.75, 1.25)),
         ((2, 2), (3, 2)),
         ((1.75, 1.25), (1.25, 1.25)),
         ((1.75, 1.25), (2.25, 0.75)),
         ((3, 2), (3, 1)),
         ((0, 2), (0, 1)),
         ((1.25, 1.25), (0.75, 0.75)),
         ((0.75, 0.75), (0, 1)),
         ((0.75, 0.75), (1, 0)),
         ((2.25, 0.75), (2, 0)),
         ((2.25, 0.75), (3, 1)),
         ((2, 0), (1, 0)),
         ((2, 0), (3, 0)),
         ((3, 1), (3, 0)),
         ((0, 1), (0, 0)),
         ((1, 0), (0, 0))]

有了这个网络,我们不需要使用任何 Voronoi 图表(虽然很顺眼)来填充 网络的细胞。

求解的基础是使用最小循环 网络的基础迭代器,然后更正每个 用于跟踪网络中实际边缘的循环(请参阅 documentation for minimum cycle basis “节点不一定按顺序返回 它们出现在循环中").

解决方案如下,假设edges 从上面:

import matplotlib.pyplot as plt
import networkx as nx

G = nx.Graph()
for edge in edges:
    G.add_edge(edge[0], edge[1])

pos = {x: x for x in G.nodes}
options = {
    "node_size": 10,
    "node_color": "lime",
    "edgecolors": "black",
    "linewidths": 1,
    "width": 1,
    "with_labels": False,
}
nx.draw_networkx(G, pos, **options)

# Fill all cells of graph
for cycle in nx.minimum_cycle_basis(G):
    full_cycle = cycle.copy()
    cycle_path = [full_cycle.pop(0)]
    while len(cycle_path) < len(cycle):
        for nb in G.neighbors(cycle_path[-1]):
            if nb in full_cycle:
                idx = full_cycle.index(nb)
                cycle_path.append(full_cycle.pop(idx))
                break
    plt.fill(*zip(*cycle_path))
plt.show()

结果图如下所示:

该算法比 Voronoi / centroid 更好地缩放 问题编辑中列出的方法,但受到影响 来自大型网络的相同低效率 (O(m^2n), 根据中的参考 documentation for minimum cycle basis).