Graphviz中的Graph of Graphs

9

我有一系列用DOT语言编码的二元组集合。我想构建一个超级图的图集,使得超级图中的每个节点都是这些二元组之一。在GraphViz框架内有没有一种方法可以实现这一点?

我知道gvpack可以让我将多个图形组装成一个.dot文件。但我不知道它是否允许我在这些图形之间声明边缘。


相关:https://dev59.com/I3I-5IYBdhLWcg3wHUdB - Gabriel Devillers
2个回答

11
gvpack不会在子图之间声明边缘。如果子图之间有共同的节点名称,gvpack会将它们重命名以避免冲突。但是,这是可以解决的。例如,给定三个.dot文件1.dot:
digraph {
    A -> B
    A -> C
    }

2.dot:

digraph {
    D -> E
    E -> F
    }

...和3.dot

digraph {
    D -> G
    G -> A
    }

运行gvpack -u 1.dot 2.dot 3.dot | dot -Tjpg -ogvp1.jpg会生成以下图形gvp1.jpg:

enter image description here

如您所见,gvpack已重新标记了重复的节点名称。但是,我们可以轻松地使用gvpack -u 1.dot 2.dot 3.dot | sed 's/_gv[0-9]\+//g' | dot -Tjpg -ogvsub.jpg进行反向重标记,生成以下图形gvsub.jpg:

enter image description here

此方法依赖于子图具有共同的节点名称,因此可能需要在子图.dot文件中插入其他节点。

(编辑:上面的解决方案显示了合并节点的图形,但未显示聚类子图。下面的解决方案显示了聚类子图。)

给定.dot文件1.dot(这些与上面的文件相同,只是我给每个有向图命名):

digraph g1 {
    A -> B
    A -> C
    }

2.dot:

digraph g2 {
    D -> E
    E -> F
    }

...和3.dot

digraph g3 {
    D -> G
    G -> A
    }

一起使用 hdr.dot

digraph GMaster {
    compound = true;
    g1 [style=invisible, height = 0, width = 0, label=""];
    g2 [style=invisible, height = 0, width = 0, label=""];
    g3 [style=invisible, height = 0, width = 0, label=""];
    g1 -> g2 [lhead=clusterg2, ltail=clusterg1];
    g1 -> g3 [lhead=clusterg3, ltail=clusterg1]

...以及tail.dot

}

使用 cat 1.dot 2.dot 3.dot | sed 's/digraph \(\w*\) *{/subgraph cluster\1 { \1/' | cat hdr.dot - tail.dot | dot -Tjpg -oclust1.jpg 命令可以生成文件 clust1.jpg:

enter image description here

因此,在头文件中,我为每个子图添加了一个不可见节点,与子图相同的名称,使用 compound=true 允许在集群之间绘制边缘。 我已指定要在群集之间绘制的边缘,并为每个不可见节点之间的每个边缘设置了 lheadltail,以确保正确的簇用于每个这些边缘的头部和尾部。 我还使用 sed 将每个子图转换为群集过程中,向每个子图添加了适当的不可见节点。

显示节点 D、G 和 A 之间的边缘,因为这些节点在群集之间是共通的。此外,它们每个都只在一个群集中显示。如果节点对于群集是唯一的,则在群集之间仅显示连接不可见节点的边缘。在下面的图表中可以看到这一点,我已重命名 3.dot 中的节点:

enter image description here

还有一个问题,我还没有完全解决。 不可见节点仍然需要一些空间,因此集群框看起来有些倾斜,因为不可见节点与可见节点并排坐着。这也意味着群集之间的边缘头部指向群集框的一侧而不是中间。目前,除非我们准备查看每个子图,并找到已经在该子图/簇中的节点,以作为该子图/簇的代表节点(即用于从该簇绘制或绘制边缘的节点)。如果有很多子图,则这可能很烦琐。

相比之下,上面使用的方法只需要我们知道集群的名称并将其插入到 hdr.dot 文件中即可。

我手工制作了此案例的 hdr.dot 文件,但是如果需要,可以使用 sedawkperlpython 从其他 .dot 文件中提取 hdr.dot 文件的内容。如果某处有关于连接哪些簇的信息,则该脚本还可以将连接群集的边缘插入到 hdr.dot 中。


感谢您的详细回复,Simon。但在我的情况下,我想将每个子图视为超级图中的节点,而不是组件。我想指定1->2和1->3之间存在一条边,并且将子图以节点的形式清晰地绘制出来,用适当的边连接组件。 - MRocklin
啊,我懂了。我认为连接子图可以通过类似于我在答案中采用的方法实现。我会仔细考虑一下。 - Simon
1
我已经编辑了我的答案,以展示如何通过边缘将聚类连接起来。 - Simon

0

处理这个问题最简单的方法是允许gvpack执行重命名,而不是尝试将其反转,以断言节点的标签。

从这里开始

digraph g1 {
  A -> B; 
  A -> C;
  A [label="A"];
  B [label="B"];
  C [label="C"];
}

并且

digraph g2 {
  D -> E
  E -> F
  D [label="D"];
  E [label="E"];
  F [label="F"];
}

并且

digraph g3 {
  D -> G
  G -> A
  D [label="D"];
  G [label="G"];
  A [label="A"];
}

你应该从你的原始第一步中得到想要的结果


这不是一个好的解决方案,因为两个 D 值没有合并。 - Bruno Adelé
是的,它们是。 - John Haugeland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接