Graphviz4S

Stella981
• 阅读 438

前言

    之前需要在Scala中用到类似python的graphviz库的功能,用来在Mxnet中可视化网络结构,

但是在网上搜索了一下,没有找到好用的库,所以就自己去把python的graphviz库中的主要功能

用Scala实现了一下,尽量的保持接口和python库的一致,也方便从python移植相关的代码到

Scala,然后我把这个小项目开源了,地址是Graphviz4S,有兴趣的朋友可以去试用一下。

接下来我会结合代码,用几个例子来介绍如何使用这个小工具。

正文

    接下来我会通过几个例子介绍Grapphviz4S,例子参考自这篇博客

1、简单例子

1.1、简单图例

    首先来看第一个例子,Scala代码如下:

    import com.liangdp.graphviz4s.Graph
    val dot = new Graph(name = "graphname")
    dot.edges(Array(
      ("a", "b"),
      ("b", "c"),
      ("b", "d"),
      ("d", "a")
    ))
    
    println(dot.source())
//    graph graphname {
//            a -- b
//            b -- c
//            b -- d
//            d -- a
//    }
    
    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    生成的结果如下:

 Graphviz4S

1.2、简单图例2

    第二个例子和上面的一样,但是布局不同,Scala代码如下:

    import com.liangdp.graphviz4s.Graph
    val dot = new Graph(name = "graphname")
    
    dot.body += "\t\trankdir=LR  //Rank Direction Left to Right"
    dot.edges(Array(
      ("a", "b"),
      ("b", "c"),
      ("b", "d"),
      ("d", "a")
    ))
    
    println(dot.source())
//    graph graphname {
//            rankdir=LR  //Rank Direction Left to Right
//            a -- b
//            b -- c
//            b -- d
//            d -- a
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    生成的结果如下:

    Graphviz4S

1.3、简单有向图

    第三个例子是一个简单的有向图,Scala代码如下:

    import com.liangdp.graphviz4s.Digraph
    val dot = new Digraph(name = "graphname")
    
    dot.edges(Array(
      ("a", "b"),
      ("b", "c"),
      ("a", "c")
    ))
    
    println(dot.source())
//    digraph graphname {
//            a -> b
//            b -> c
//            a -> c
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    生成的结果如下:

    Graphviz4S     

1.4、带标签的简单有向图

    第四个例子给有向图的边加上标签,对应的Scala代码如下:    

    import com.liangdp.graphviz4s.Digraph
    val dot = new Digraph(name = "graphname")
    
    dot.node(name = "T", label = "Teacher")
    dot.node(name = "P", label = "Pupil")
    
    import scala.collection.mutable.Map
    dot.edge(tailName = "T", headName = "P", label = "Instructions",
        attrs = Map("fontcolor" -> "darkgreen"))
    
    println(dot.source())
//    digraph graphname {
//        "T" [label="Teacher" ]
//        "P" [label="Pupil" ]
//            T -> P [label="Instructions"  fontcolor=darkgreen]
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    生成的结果如下:

    Graphviz4S

1.5、总结

    Scala代码:    

    import com.liangdp.graphviz4s.Digraph
    val dot = new Digraph(name = "summary")
    
    dot.node(name = "start", label = "Start with a Node")
    import scala.collection.mutable.Map
    dot.node(name = "next", label = "Choose your shape", attrs = Map("shape" -> "box"))
    dot.node(name = "warning", label = "Don't go overboard",
        attrs = Map("color" -> "Blue", "fontcolor" -> "Red", "fontsize" -> "24",
            "style" -> "filled", "fillcolor" -> "green", "shape" -> "octagon"))
    dot.node(name = "end", label = "Draw your graph!",
        attrs = Map("shape" -> "box", "style" -> "filled", "fillcolor" -> "yellow"))

    dot.edge(tailName = "start", headName = "next")
    dot.edge(tailName = "start", headName = "warning")
    dot.edge(tailName = "next", headName = "end", label = "Getting Better...")

    println(dot.source())
//    digraph summary {
//        "start" [label="Start with a Node" ]
//        "next" [label="Choose your shape"  shape=box]
//        "warning" [label="Don't go overboard"  fontsize=24 color=Blue fillcolor=green shape=octagon fontcolor=Red style=filled]
//        "end" [label="Draw your graph!"  fillcolor=yellow shape=box style=filled]
//            start -> next []
//            start -> warning []
//            next -> end [label="Getting Better..." ]
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    生成的结果如下:

Graphviz4S

2、高级例子

2.1、少写一点代码

    单独地去定义每一个节点的属性很浪费时间,下面这个技巧能够让你coding的速度快一点。

Scala代码如下:

    import com.liangdp.graphviz4s.Digraph
    val dot = new Digraph(name = "hierarchy")
    
    // increases the separation between nodes
    dot.body += "\tnodesep=1.0"
    import scala.collection.mutable.Map
    //All nodes will this shape and colour
    dot.attr("node", attrs =
      Map("color" -> "Red", "fontname" -> "Courier", "shape" -> "box"))
    //All the lines look like this
    dot.attr("edge", attrs = Map("color" -> "Blue", "style" -> "dashed"))
    
    dot.edges("Headteacher", Array("Deputy1", "Deputy2", "BusinessManager"))
    dot.edges("Deputy1", Array("Teacher1", "Teacher2"))
    dot.edge("BusinessManager", "ITManager")
    // Put them on the same level
    dot.body += "\t{rank=same;ITManager Teacher1 Teacher2}"
    
    println(dot.source())
//    digraph hierarchy {
//        nodesep=1.0 
//        node [ fontname=Courier color=Red shape=box]
//        edge [ color=Blue style=dashed]
//            Headteacher -> Deputy1
//            Headteacher -> Deputy2
//            Headteacher -> BusinessManager
//            Deputy1 -> Teacher1
//            Deputy1 -> Teacher2
//            BusinessManager -> ITManager []
//        {rank=same;ITManager Teacher1 Teacher2}
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    结果如下:

    Graphviz4S

2.2、html

    Scala代码如下:

    import com.liangdp.graphviz4s.Digraph
    val dot = new Digraph(name = "structs")
    
    import scala.collection.mutable.Map
    dot.attr("node", attrs = Map("shape" -> "record"))
    dot.node("struct1", label = """"<f0> left|<f1> mid\ dle|<f2> right"""")
    dot.node("struct2", label = """"{<f0> one|<f1> two\n\n\n}"""",
        attrs = Map("shape" -> "Mrecord"))
    dot.node("struct3", label = """"hello\nworld |{ b |{c|<here> d|e}| f}| g | h"""")
    
    dot.edge("struct1:f1", "struct2:f0")
    dot.edge("struct1:f0", "struct3:here")

    println(dot.source())
//    digraph structs {
//        node [ shape=record]
//        "struct1" [label="<f0> left|<f1> mid\ dle|<f2> right" ]
//        "struct2" [label="{<f0> one|<f1> two\n\n\n}"  shape=Mrecord]
//        "struct3" [label="hello\nworld |{ b |{c|<here> d|e}| f}| g | h" ]
//            struct1:f1 -> struct2:f0 []
//            struct1:f0 -> struct3:here []
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    结果如下:

    Graphviz4S

2.3、有限状态机

     Scala代码如下:

    import com.liangdp.graphviz4s.Digraph
    val dot = new Digraph(name = "structs")
    
    dot.body += "\trankdir=LR"
    dot.body += s"""${"\t"}size="8,5""""
    
    import scala.collection.mutable.Map
    dot.attr("node", attrs = Map("shape" -> "circle"))
    dot.edge(tailName = "S0", headName = "S1", label = """"Lift Nozzle"""")
    dot.edge(tailName = "S1", headName = "S0", label = """"Replace Nozzle"""")
    dot.edge(tailName = "S1", headName = "S2", label = """"Authorize Pump"""")
    dot.edge(tailName = "S2", headName = "S0", label = """"Replace Nozzle"""")
    dot.edge(tailName = "S2", headName = "S3", label = """"Pull Trigger"""")
    dot.edge(tailName = "S3", headName = "S2", label = """"Release Trigger"""")

    println(dot.source())
//    digraph structs {
//        rankdir=LR
//        size="8,5"
//        node [ shape=circle]
//            S0 -> S1 [label="Lift Nozzle" ]
//            S1 -> S0 [label="Replace Nozzle" ]
//            S1 -> S2 [label="Authorize Pump" ]
//            S2 -> S0 [label="Replace Nozzle" ]
//            S2 -> S3 [label="Pull Trigger" ]
//            S3 -> S2 [label="Release Trigger" ]
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    结果如下:

    Graphviz4S

2.4、数据流示意图

    Scala代码如下:    

    import com.liangdp.graphviz4s.Digraph
    val dot = new Digraph(name = "dfd")
    
    import scala.collection.mutable.Map
    dot.attr("node", attrs = Map("shape" -> "record"))
    dot.node("store1", label = """"<f0> left|<f1> Some data store"""")
    dot.node("proc1", label = """"{<f0> 1.0|<f1> Some process here\n\n\n}"""",
        attrs = Map("shape" -> "Mrecord"))
    dot.node("enti1", label = "Customer",
        attrs = Map("shape" -> "box"))

    dot.edge(tailName = "store1:f1", headName = "proc1:f0")
    dot.edge(tailName = "enti1", headName = "proc1:f0")

    println(dot.source())
//    digraph dfd {
//        node [ shape=record]
//        "store1" [label="<f0> left|<f1> Some data store" ]
//        "proc1" [label="{<f0> 1.0|<f1> Some process here\n\n\n}"  shape=Mrecord]
//        "enti1" [label="Customer"  shape=box]
//            store1:f1 -> proc1:f0 []
//            enti1 -> proc1:f0 []
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    结果如下:

    Graphviz4S

2.5、数据流示意图2

    Scala代码如下:    

    import com.liangdp.graphviz4s.Digraph
    val dot = new Digraph(name = "dfd2")
    
    import scala.collection.mutable.Map
    dot.attr("node", attrs = Map("shape" -> "record"))
    
    val subgraph0 = new Digraph(name = "level0")
    subgraph0.node("enti1", label = "Customer", Map("shape" -> "box"))
    subgraph0.node("enti2", label = "Manager", Map("shape" -> "box"))
    
    val subgraph1 = new Digraph(name = "cluster_level1")
    subgraph1.body += s"""${"\t"}label ="Level 1""""
    subgraph1.node("proc1", label = """"{<f0> 1.0|<f1> One process here\n\n\n}"""",
        attrs = Map("shape" -> "Mrecord"))
    subgraph1.node("proc2", label = """"{<f0> 2.0|<f1> Other process here\n\n\n}"""",
        attrs = Map("shape" -> "Mrecord"))
    subgraph1.node("store1", label = """"<f0>    |<f1> Data store one"""")
    subgraph1.node("store2", label = """"<f0>   |<f1> Data store two"""")
    subgraph1.body += "\t{rank=same; store1, store2}"
    
    dot.subGraph(subgraph0)
    dot.subGraph(subgraph1)
    
    dot.edge("enti1", "proc1")
    dot.edge("enti2", "proc2")
    dot.edge("store1", "proc1")
    dot.edge("store2", "proc2")
    dot.edge("proc1", "store2")
    dot.edge("store2", "proc1")

    println(dot.source())
//    digraph dfd2 {
//        node [ shape=record]
//        subgraph level0 {
//            "enti1" [label="Customer"  shape=box]
//            "enti2" [label="Manager"  shape=box]
//        }
//        subgraph cluster_level1 {
//            label ="Level 1"
//            "proc1" [label="{<f0> 1.0|<f1> One process here\n\n\n}"  shape=Mrecord]
//            "proc2" [label="{<f0> 2.0|<f1> Other process here\n\n\n}"  shape=Mrecord]
//            "store1" [label="<f0>    |<f1> Data store one" ]
//            "store2" [label="<f0>   |<f1> Data store two" ]
//            {rank=same; store1, store2}
//        }
//            enti1 -> proc1 []
//            enti2 -> proc2 []
//            store1 -> proc1 []
//            store2 -> proc2 []
//            proc1 -> store2 []
//            store2 -> proc1 []
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    结果如下:

    Graphviz4S

2.6、对象继承

    Scala代码如下:        

    import com.liangdp.graphviz4s.Digraph
    val dot = new Digraph(name = "obj")
    
    import scala.collection.mutable.Map
    dot.attr("node", attrs = Map("shape" -> "record"))

    dot.body += s"""${"\t"}rankdir="BT""""
    
    dot.node("teacher", label = """"{<f0> Teacher|<f1> \n  |<f2> \n   }"""")
    dot.node("course", label = """"{<f0> Course|<f1> \n  |<f2> \n   }"""")
    dot.node("student", label = """"{<f0> Student|<f1> \n  |<f2> \n   }"""")
    dot.node("lesson", label = """"{<f0> Lesson |<f1> \n  |<f2> \n   }"""")
    dot.node("tutorial", label = """"{<f0> Tutorial|<f1> \n  |<f2> \n   }"""")
    dot.node("assessment", label = """"{<f0> Assessment|<f1> \n  |<f2> \n   }"""")
    dot.node("coursework", label = """"{<f0> Coursework|<f1> \n  |<f2> \n   }"""")
    dot.node("exam", label = """"{<f0> Exam|<f1> \n  |<f2> \n   }"""")

    dot.body += "\t{rank=same; teacher course student}"
    
    dot.edge("teacher", "course", attrs = Map("dir" -> "forward", "arrowhead" -> "none",
        "arrowtail" -> "normal", "headlabel" -> """"1"""", "taillabel" -> """"1..""""))
    dot.edge("student", "course", attrs = Map("dir" -> "forward", "arrowhead" -> "none",
        "arrowtail" -> "normal", "headlabel" -> """"1"""", "taillabel" -> """"1..""""))
    dot.edge("lesson", "course", attrs = Map("dir" -> "forward", "arrowhead" -> "diamond",
        "arrowtail" -> "normal"))
    dot.edge("tutorial", "course", attrs = Map("dir" -> "forward", "arrowhead" -> "diamond",
        "arrowtail" -> "normal"))
    dot.edge("assessment", "course", attrs = Map("dir" -> "forward", "arrowhead" -> "diamond",
        "arrowtail" -> "normal"))
    dot.edge("coursework", "assessment")
    dot.edge("exam", "assessment")

    println(dot.source())
//    digraph obj {
//        node [ shape=record]
//        rankdir="BT"
//        "teacher" [label="{<f0> Teacher|<f1> \n  |<f2> \n   }" ]
//        "course" [label="{<f0> Course|<f1> \n  |<f2> \n   }" ]
//        "student" [label="{<f0> Student|<f1> \n  |<f2> \n   }" ]
//        "lesson" [label="{<f0> Lesson |<f1> \n  |<f2> \n   }" ]
//        "tutorial" [label="{<f0> Tutorial|<f1> \n  |<f2> \n   }" ]
//        "assessment" [label="{<f0> Assessment|<f1> \n  |<f2> \n   }" ]
//        "coursework" [label="{<f0> Coursework|<f1> \n  |<f2> \n   }" ]
//        "exam" [label="{<f0> Exam|<f1> \n  |<f2> \n   }" ]
//        {rank=same; teacher course student}
//            teacher -> course [ arrowtail=normal dir=forward taillabel="1.." arrowhead=none headlabel="1"]
//            student -> course [ arrowtail=normal dir=forward taillabel="1.." arrowhead=none headlabel="1"]
//            lesson -> course [ arrowtail=normal dir=forward arrowhead=diamond]
//            tutorial -> course [ arrowtail=normal dir=forward arrowhead=diamond]
//            assessment -> course [ arrowtail=normal dir=forward arrowhead=diamond]
//            coursework -> assessment []
//            exam -> assessment []
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    结果如下:

    Graphviz4S

2.7、关系型实体

     Scala代码如下: 

    import com.liangdp.graphviz4s.Digraph
    val dot = new Digraph(name = "ER")
    
    import scala.collection.mutable.Map
    dot.attr("node", attrs = Map("shape" -> "box"))

    dot.node("Book")
    dot.node("Customer")
    dot.node("Loan")
    
    dot.body += "\t{rank=same;Book,Customer,Loan}"

    dot.edge("Book", "Loan", attrs = Map("dir" -> "forward",
        "arrowhead" -> "crow", "arrowtail" -> "normal"))
    dot.edge("Customer", "Loan", attrs = Map("dir" -> "forward",
        "arrowhead" -> "crow", "arrowtail" -> "normal"))
        
    println(dot.source())
//    digraph ER {
//        node [ shape=box]
//        "Book" []
//        "Customer" []
//        "Loan" []
//        {rank=same;Book,Customer,Loan}
//            Book -> Loan [ arrowtail=normal dir=forward arrowhead=crow]
//            Customer -> Loan [ arrowtail=normal dir=forward arrowhead=crow]
//    }

    dot.render(fileName = "graphname.gv", directory = ".", view = true)

    结果:

    Graphviz4S

结尾

    通过以上例子的介绍,相信读者都能够了解如何使用这个小工具了,不过这个小工具还有很多

需要完善的地方,也欢迎感兴趣的朋友一起来完善它。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这