使用 pydot 的紧凑图形可视化

Compact graph-vizualization using pydot

我想可视化一个“线性”有向图,其布局如下:

所有入度和出度都是1(当然第一个和最后一个除外)。标签的长度不同,所以我无法轻松计算出一行或另一行适合多少个节点。我目前的代码是这样的。

import networkx as nx
from networkx.drawing.nx_pydot import to_pydot

G = nx.DiGraph()
G.add_node("XYZ 1.0")
for i in range(1, 20):
    G.add_node(f'XYZ 1.{i}', style='filled', fillcolor='skyblue')
    G.add_edge(f'XYZ 1.{i-1}', f'XYZ 1.{i}')

# set defaults
G.graph['graph'] = {'rankdir': 'LR'}
G.graph['node'] = {'shape': 'rectangle'}
G.graph['edges'] = {'arrowsize': '4.0'}

pydt = to_pydot(G)
prog = 'dot'
file_name = f'nx_graph_{prog}.png'
pydt.write(file_name, prog=prog, format="png")

到目前为止我在一个需要运行在Pythondocker容器中的项目中使用networkx,所以我想使用pydotNetworkx,如果可能的话。

在一些 graphviz 程序中,如果我理解正确的话,我可以设置坐标,但是对于设置坐标,我应该知道框的宽度以避免框重叠。

我设法找到了一种使用 pydot 执行此操作的方法。我们可以使用 write_dot 函数创建一个带有坐标的点文件。回读它,我们可以获得 dot 程序创建的坐标(以及宽度和高度)。我们可以以某种方式计算新坐标并在 networkx 有向图中修改它们。再次转换为 pydot.Dot 对象,最后,我们可以使用 neato-n 选项来创建图形,这样我们就可以使用我们设置的坐标。可以在下面看到一个工作代码。

import networkx as nx
from networkx.drawing.nx_pydot import to_pydot
import pydot
from typing import List

G = nx.DiGraph()
G.add_node(0, label="XYZ 1.0")
for i in range(1, 20):
    G.add_node(i, label=f'XYZ 1.{i}')
    G.add_edge(i - 1, i)

# set defaults
G.graph['graph'] = {'rankdir': 'LR'}
G.graph['node'] = {'shape': 'rectangle'}
G.graph['edges'] = {'arrowsize': '4.0'}

pydt = to_pydot(G)
dot_data = pydt.create_dot()

pydt2 = pydot.graph_from_dot_data(dot_data.decode('utf-8'))[0]


def get_position(node):
    pydot_node = pydt2.get_node(str(node))[0]
    return [float(i) for i in pydot_node.get_attributes().get("pos")[1:-1].split(',')]


def fix_position(position: List, w: float = 1000, shift: float = 80):
    x_orig, y_orig = position
    n = int(x_orig / w)
    y = y_orig - n * shift
    remain_x = x_orig - n * w
    if n % 2 == 0:
        x = remain_x
    else:
        x = w - remain_x
    return x, y


def refresh_coordinates_using_x():
    for node in G.nodes:
        position = get_position(node)
        x, y = fix_position(position)
        pos = f'"{x},{y}!"'
        G.nodes[node]['pos'] = pos


refresh_coordinates_using_x()

pydt3 = to_pydot(G)
file_name = f'nx_graph_neato.png'
pydt3.write(file_name, prog=["neato", "-n"], format="png")

如果您想根据宽度计算节点的位置,您需要知道坐标以磅为单位,而宽度以英寸为单位。 1英寸是72分。

结果将与此类似。