【问题标题】:How to get rank of nodes after processing with Graphviz dot?使用 Graphviz dot 处理后如何获取节点的排名?
【发布时间】:2019-12-04 22:28:25
【问题描述】:

我使用 dot 来渲染图形,效果还可以。

现在我需要以某种方式获取点分配给每个节点的排名,有没有办法做到这一点?

例如从这个 .dot 文件:

digraph D {
    Ivan -> Herbert [label="15,16"];
    Ivan -> Diego [label="23", color="slategray"];
    Roberto -> Herbert [label="17,18"];
    Roberto -> Ivan [label="19,20"];
    Diego -> Roberto [label="21", color="slategray", style=dashed, color=red, constraint=false]
    {rank=max;}
}

我想获得如下信息:

rank of "Roberto" is 1
rank of "Ivan" is 2
rank of "Diego" is 3
rank of "Herbert" is 3

其中节点的等级是它在图形渲染中的深度,即顶部节点的等级为 1,其子节点为 2,等等。

请注意,我的图表通常更复杂,并且总是包含循环,并且视觉布局显示给用户,因此“自己动手”的方法不在表格中,因为每个节点的等级需要与点渲染。

我目前正在使用 python,但我可以使用任何其他工具来实现这一点。

【问题讨论】:

  • that 帮忙吗?

标签: graphviz dot


【解决方案1】:

这是(非常简单的测试)GVPR (https://www.graphviz.org/pdf/gvpr.1.pdf) 程序,它似乎可以满足您的需求。
一旦计算了 X-Y 坐标,Dot 似乎就不会保留排名数字。所以这个程序反向计算一个节点 Y(或 X)坐标的秩数。
Linux命令行为:dot -Tdot yourgraph.gv | gvpr -f listRank.gvpr

listRank.gvpr 程序:

BEGIN {
  int i, j, N, R=0,  nIndx=0, list[string];
  string rankList[double];
  string nodeIndx[int];
  string str1, RankDir="TB"; 
  node_t aNode, thisNode[];
  double dist;

  void setRank(){
    R++;
    str1=substr(rankList[dist],1);     // strip leading delimiter
    unset(nodeIndx);                      // delete array of node indices
    N=split(str1, nodeIndx, "|");
    for (i=0; i<N; i++){              // for each node in this rank (w/ same Y/X pos)
      j=nodeIndx[i];
      aNode=thisNode[j];
      print ("rank of \"", aNode.name, "\"  is ",  R);
    }
  }
}
BEG_G{
  if (hasAttr($G, "rankdir") && $G.rankdir!="")
    RankDir=$G.rankdir;
  print ("// RANKDIR: ", RankDir);
}
N {
  thisNode[++nIndx]=$;  // index the nodes
  if (RankDir=="LR|RL"){        // ksh pattern matching
    dist=$.X;
  } else {
    dist=$.Y;
  }
  // build a string containing all node indices, based on (indexed by) Y/X value
  rankList[dist]=sprintf("%s|%s", rankList[dist], nIndx);
}
END_G{
  // for each rank, in sorted order
  if (RankDir=="TB|RL"){        //      ksh pattern matching
    forr (rankList[dist]){      //  decreasing sort
      setRank();
    }
  }else{
    for (rankList[dist]){       // increasing sort
      setRank();
    }
  }
}

此文件已失效(https://www.graphviz.org/Gallery/directed/cluster.html)

rank of "start"  is 1
rank of "a0"  is 2
rank of "b0"  is 2
rank of "a1"  is 3
rank of "b1"  is 3
rank of "a2"  is 4
rank of "b2"  is 4
rank of "a3"  is 5
rank of "b3"  is 5
rank of "end"  is 6

【讨论】:

    【解决方案2】:

    我确信有一个更有效的算法,但您可以在networkx 中执行此操作,首先获取所有“根”节点(即没有“入”边的节点),然后在来自每个节点的图表,随时定义排名。

    我不知道它是否与 DOT 定义的完全相同,但它确实构建了一个准确的拓扑排名。

    代码:

    from collections import defaultdict
    import networkx as nx
    
    graph = nx.drawing.nx_agraph.read_dot('graph.dot')
    roots = []
    ranks = defaultdict(int)
    for node in graph.nodes:
        if not list(graph.predecessors(node)):
            roots.append(node)
            ranks[node] = 1
    
    for root in roots:
        for (head, tail) in nx.bfs_edges(graph, root):
            ranks[tail] = max(ranks[tail], ranks[head] + 1)
    
    print(ranks)
    

    输出:

    defaultdict(<class 'int'>, {'a': 1, 'e': 1, 'b': 2, 'c': 3, 'd': 3})
    

    【讨论】:

    • @brentertainer:谢谢,但我需要保证排名输出与 dot 渲染的完全相同。我添加了关于排名的含义和更好的示例的解释,无论如何我可能会使用类似您的答案的内容,以防万一
    • @Roberto,这是一个很好的问题。我希望它得到更多的关注。
    猜你喜欢
    • 2011-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 1970-01-01
    • 2016-12-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多