You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让networkx生成的tikz有向无环图按节点w属性分层?

问题:基于节点w属性的有向无环图TikZ分层绘制

我用NetworkX创建了一个带权有向无环图G = networkx.DiGraph(),每个节点都有非负整数属性w,这个图代表偏序集——如果节点n1到n2有路径,通常满足G.nodes[n1]["w"] < G.nodes[n2]["w"],但w不是图位置的直接函数,比如指向同一节点的两个节点可以有不同w值,只要比来源节点的w大就行。

现在需要把这个图转成LaTeX的TikZ图形,要求不同w值的节点处于不同高度,相同w值的节点在同一高度。我写了示例代码,但生成的图里节点C(w=2)和B(w=1)在同一层,没法体现1<2<3的层级关系,实际有50个节点,手动调整太麻烦,求自动实现的简便方法,高度不用完全精确,能区分不同w就行。

示例代码:

import networkx
import dot2tex


nodes = [
    ["A", {"w": 0}],
    ["B", {"w": 1}],
    ["C", {"w": 2}],
    ["D", {"w": 3}],
        ]

edges = [
    ["A", "B"],
    ["A", "C"],
    ["B", "D"],
    ["C", "D"],
]

G = networkx.DiGraph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
    
A = networkx.nx_agraph.to_agraph(G)
dot_string = A.string()

latex_string = dot2tex.dot2tex(
    dot_string,
    preproc=True,
    docpreamble=r"\usepackage{amsmath,amssymb,amsthm,bm}",
    format="tikz"
)
latex_string = dot2tex.dot2tex(
    latex_string,
    figonly=False,
    docpreamble=r"\usepackage{amsmath,amssymb,amsthm,bm}",
    autosize=True,
    crop=True,
    valignmode="dot",
    format="tikz"
)

print(latex_string)

with open("test.tex", "w") as f:
    f.write(latex_string)

解决方案:利用Graphviz的分层布局+节点属性映射

核心思路是给Graphviz指定节点的层级(rank),直接用节点的w值作为rank值,这样Graphviz会自动把相同w的节点放在同一层,不同w的按数值高低分层。

步骤1:给Graphviz图的节点添加rank属性

在把NetworkX图转成Graphviz的AGraph后,遍历每个节点,将其w属性赋值给Graphviz节点的rank属性。Graphviz的rank支持整数,直接映射即可。

步骤2:调整dot2tex参数,保留rank布局

修改代码,确保Graphviz的布局规则被正确传递给TikZ,避免二次调用dot2tex导致布局信息丢失。

修改后的完整代码

import networkx
import dot2tex

nodes = [
    ["A", {"w": 0}],
    ["B", {"w": 1}],
    ["C", {"w": 2}],
    ["D", {"w": 3}],
]

edges = [
    ["A", "B"],
    ["A", "C"],
    ["B", "D"],
    ["C", "D"],
]

G = networkx.DiGraph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)

# 转成Graphviz的AGraph,并设置节点rank
A = networkx.nx_agraph.to_agraph(G)
for node in G.nodes(data=True):
    node_name, attrs = node
    # 将w值设为Graphviz节点的rank属性
    A.get_node(node_name).attr["rank"] = str(attrs["w"])

dot_string = A.string()

# 直接生成完整的LaTeX文档
latex_string = dot2tex.dot2tex(
    dot_string,
    figonly=False,
    docpreamble=r"\usepackage{amsmath,amssymb,amsthm,bm}",
    autosize=True,
    crop=True,
    valignmode="dot",
    format="tikz",
    preproc=True  # 启用预处理器,确保Graphviz布局生效
)

print(latex_string)

with open("test.tex", "w") as f:
    f.write(latex_string)

关键说明

  • rank属性作用:Graphviz会根据rank值强制分层,rank值小的节点在上方(默认布局方向),大的在下方,完美匹配偏序集的w值递增关系。
  • 单次调用dot2tex:原来的两次调用会丢失Graphviz的布局信息,改成一次调用即可保留rank设置。
  • preproc=True:确保dot2tex先让Graphviz处理布局,再转成TikZ代码,rank的分层规则会被正确应用。

可选优化

如果需要调整布局方向或层级间距,可添加全局Graphviz属性:

# 设置布局方向为从左到右(默认从上到下)
A.graph_attr["rankdir"] = "LR"
# 调整层级之间的间距(单位:英寸)
A.graph_attr["ranksep"] = "0.8"

内容的提问来源于stack exchange,提问作者Bulkilol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 22:09:58