如何在Networkx中画出漂亮的毛毛虫树?

How to draw Caterpillar Trees in Networkx beautifully?

我想用 Networkx 画毛毛虫树。有没有办法画出书脊在一条直线上,叶子清晰可见?

来自维基百科的图片是我想画的东西的一个很好的例子。

我们可以使用random_lobster网络生成毛毛虫网络 生成函数,参数 p2 保持为零。

import itertools
import networkx as nx
import matplotlib.pyplot as plt

spine_sz = 5
G = nx.random_lobster(spine_sz, 0.65, 0, seed=7)

第二部分,按照画出spine的方式画网络 在一条直线上,叶子清晰可见需要相当多的 计算。

首先,我们必须确定生成的网络的主干是什么。 由于网络是一棵树,这并不困难。我们将使用第一个 与直径一样长的路径。

eccs = nx.eccentricity(G)
diameter = max(eccs.values())
outliers = tuple(node for node in eccs if eccs[node] == diameter)
for combo in itertools.combinations(outliers, 2):
    path = nx.shortest_path(G, combo[0], combo[1])
    if len(path) == diameter+1:
        break

其次,我们必须估计网络所需的 space,假设 我们将在脊柱上方和下方绘制叶子,均匀分布 可能。

def node_space(nleaves):
    return 1+int((nleaves-1)/2)

nb_leaves = {}
for node in path:
    neighbors = set(G.neighbors(node))
    nb_leaves[node] = neighbors - set(path)
max_leaves = max(len(v) for v in nb_leaves.values())
space_needed = 1 + sum(node_space(len(v)) for v in nb_leaves.values())

第三,有了这个间距,我们就可以画出书脊了,每个节点都有 脊柱,在它周围画出叶子,将它们分布在上方和下方 节点,同时还在“脊柱space”中水平分布它们 预订的。我们将使用偶数的节点号来确定它是否会出现 在脊柱上方或下方,为每个后续节点交替使用 树叶;后者通过反转 yhop 变量。

def count_leaves(nleaves, even):
    leaf_cnt = int(nleaves/2)
    if even:
        leaf_cnt += nleaves % 2
    return leaf_cnt

def leaf_spacing(nleaves, even):
    leaf_cnt = count_leaves(nleaves, even)
    if leaf_cnt <= 1:
        return 0
    return 1 / (leaf_cnt-1)

xhop = 2 / (space_needed+2)
yhop = 0.7
pos = {}
xcurr = -1 + xhop/4
for node in path:
    pos[node] = (xcurr, 0)
    if len(nb_leaves[node]) > 0:
        leaves_cnt = len(nb_leaves[node])
        extra_cnt = node_space(leaves_cnt) - 1
        extra_space = xhop * extra_cnt / 2
        xcurr += extra_space
        pos[node] = (xcurr, 0)
        l0hop = 2 * extra_space * leaf_spacing(leaves_cnt, True)
        l1hop = 2 * extra_space * leaf_spacing(leaves_cnt, False)
        l0curr = xcurr - extra_space
        l1curr = xcurr - extra_space
        if l1hop == 0:
            l1curr = xcurr
        for j,leaf in enumerate(nb_leaves[node]):
            if j % 2 == 0:
                pos[leaf] = (l0curr, yhop)
                l0curr += l0hop
            else:
                pos[leaf] = (l1curr, -yhop)
                l1curr += l1hop
        yhop = -yhop
        xcurr += xhop * extra_cnt / 2
    prev_leaves = len(nb_leaves[node])
    xcurr += xhop

最后一部分是图表的实际绘制,有一些选项 设置颜色和尺寸。

options = {
    "font_size": 9,
    "node_size": 300,
    "node_color": "lime",
    "edgecolors": "black",
    "linewidths": 1,
    "width": 1,
}
nx.draw_networkx(G, pos, **options)
ax = plt.gca()
ax.margins(0.10)
plt.axis("off")
plt.show()

结果图如下所示: