DiagrammeR 〜RでGraphvizを使う~
パワポでフローチャートや、ネットワーク図を作ると、ノードやエッジの位置が微妙にずれて、面倒だなと感じる人はいませんか。そんな時は、Graphvizを使用しましょう。
Graphvizを使うと、DOT言語で書かれたグラフを画像に出力できます。R言語では、DiagrammeRのgrViz関数で、DOT言語のテキストを画像に出力します。
目次:
DiagrammeR::grViz()
DiagrammeRではgrViz関数をつかって、DOT言語で記述されたグラフ構造を出力します。
簡単なサンプルです。grViz関数の引数部分(ダブルクォーテーションの中)がDOT言語です。
library(DiagrammeR) grViz(" digraph test { A -> B } ")
出力はこうなります。
Graphviz本家サイトにも沢山のサンプルがありますが、実際思ったとおりのグラフを出力しようと思うと細かい設定で詰まることが多く、結局ググってサンプルコードを探してくることが多いです。ここでは、自分がよく使う設定等を補足しながら、Graphvizの使い方をまとめておきます。
Graphvizサイト: Graphviz - Graph Visualization Software
DOT言語
DOT は、プレーンテキストを用いてデータ構造としてのグラフを表現するための、データ記述言語の一種である。
DOTの構造
digraph 名前 {}
digraphの名前は、予約語を指定するとエラーがでます。Rstudioでは.gv/.dot拡張子のファイルは、シンタックスハイライトが使えるので、予約語かどうかが分かりやすくて良いと思います。
graph, node , edgeについてそれぞれ指定していきます。
- graph [ 属性=値, ...]
- node [ 属性=値, ...]
- edge [ 属性=値, ...]
と記載していきます。
digraph test { graph[rankdir = LR, label ="fig. 1"] node [shape = box] A; B; edge[color = red, arrowsize = 1.5, arrowhead = normal] A -> B }
RstudioでDOT言語
上記のようにgrViz関数の引数としてDOT言語のテキストを記述するサンプルがよく書かれていますが、これだとdigraph以下がハイライトされません。長いコードではタイポしやすくなるので、.gvまたは.dot拡張子で外部ファイルとしておいた方がシンタックスハイライトを利用できるので良いかと思います。Rstudioでは、.gv(.dot)ファイルは、sourceペイン右上にプレビューボタンがでて、ボタンを押すとgrViz関数を呼び出して自動でRstudioのviewerペインに出力してくれます。test.dotファイルなら、DiagrammeR::grViz("test.dot")が実行されます。
Graphviz Attributes
http://204.178.9.49/content/attrs
Graph属性
サンプル
digraph graph_attribute { graph[rankdir = LR, label ="fig. 1", labelloc = t # t, b fontsize = 10, fontcolor = red, bgcolor = darkgreen, nodesep = 0.1, ranksep = 0.1] }
rankdir : グラフ全体の方向をTB(top -> bottom),LR(left -> right)等変更。
label : ラベル名。
labelloc : ラベルの位置指定。t(top), b(bottom)で指定。
bgcolor : バックグラウンドカラー。
nodesep : ノードの間隔。
ranksep : ランクの間隔。
Node属性
color属性
digraph node_color { node[style = "filled", color = IndianRed] IndianRed; }
- カラーサンプル
shape属性
digraph node_shape { node[shape = box] box; }
- shapeサンプル
style属性
digraph node_style { node[shape = box] node[style = solid] A node[style = dashed] B node[style = dotted] D node[style = bold] E node[style = filled] F node[style = striped, fillcolor = "Yellow1:Yellow2:Yellow3:Yellow4"] G node[shape = circle , style = "dashed, wedged", fillcolor = "Tomato1:Tomato2:Tomato3:Tomato4"] H }
- スタイルサンプル
rank属性
digraph node_rank { groupA -> groupB -> groupC A; B; C; D; E; F; G; H; I {rank = same; groupA; A; B; C;} {rank = same; groupB; D; E; F;} {rank = same; groupC; G; H; I} }
label属性
labelの値にはHTMLっぽい記述をすることが出来ます。
digraph test_graph { node[shape = rect, color = black, label = <<I>italic</I>>] A node[label = <10<sup>2</sup>>] B }
ただし、<>で全体を囲む必要があるみたいです。すべてのタグが使えるわけでも無いみたい…難しい。
特殊な文字は、node[label = "&文字列;"]みたいな感じで出力できるものもあります。
digraph test_graph { node[shape = box] node[label = "Σ Π α β γ δ ε"] A node[label = "θ κ λ μ φ ω"] B node[label = "♠ ♡ ♢ ♣ ♪ 〒 "] C }
image属性
image属性でファイルパスを指定すると画像を出力できるはずだが、僕の環境のDiagrammeR::grviz()では下記の.dotファイルはうまく出力できませんでした。
digraph ramen { rankdir = LR node[penwidth = 0, fontname = helvetica, labelloc = t, fontsize = 30, fontcolor = darkgreen] node[image = "img/sio.jpg"] sio; node[image = "img/syouyu.jpg"] syouyu; sio -> syouyu }
いつもはUbuntu16.04にRstudio severをインストールして使用しているので、
$ sudo apt install graphviz
でGraphvizをインストールして。
.dotファイルを、Terminalからコマンドで出力して対応
$ dot -Tpng ramen.dot -o ramen.png
最近のRstudioはTerminalペインもあり、気軽にコマンド入力もできるので、DOTファイルからそのまま出力して今回はごまかしました。(DiagrammeRからできるひと教えてください…)
Edge属性
arrowhead属性
digraph edge_arrowhead { edge[arrowhead = diamond] A -> B; }
arrowheadサンプル
- arrowhead名の前に"o"をつけると中抜けになります。
- "r","l"をつけると、左右半分のarrowheadになります。
- arrowhead名をつづけて書くと、arrowheadを繋げて出力できます。
dir, arrowsize, color, penwidth属性
digraph edge_arrowhead { edge[dir= both, arrowsize = 0.5, color = red, penwidth = .5] A -> B; }
dir, size, color, penwidthサンプル
port属性
digraph edge_arrowhead { edge[tailport = n] A -> B; }
portサンプル
Subgraph
digraph subgraph_label { rankdir = TB subgraph cluster0{ A -> B label = "group0" {rank = same; A; B;} } subgraph Cluster1{ C -> D -> E label = "group1" {rank = same; D; E;} } B -> C }
subugraphでいくつかのnodeをグループ化できます。subgraph名は、小文字のcから始まるcluster**の場合に枠線がつきますが、その他の名前では枠が消えます。
subgraphからnode、nodeからsubgraph、subgraphからsubgraphへもedgeを繋ぐことができます。
digraph G { compound=true; subgraph cluster0 { b -> d; c -> d; } subgraph cluster1 { e -> g; e -> f; } b -> f [lhead=cluster1]; d -> e; c -> g [ltail=cluster0, lhead=cluster1]; c -> e [ltail=cluster0]; }
ちなみに、subgraphを重ねることはできないみたいです。
DOT言語で、どんなグラフも作成できるわけではないですが、何度か作り直しながら使用していくようなフローチャートや、ネットワーク図は、パワポで作るより修正が楽だと思います。
Enjoy!
参考
DiagrammeR
Graphviz - Graph Visualization Software
DiagrammeRと仲良くなった話ーグラフィカルモデルのためのDiagrammeR速習ー
【みんなの知識 ちょっと便利帳】使いたいときの HTML特殊文字 & 機種依存文字 - ギリシャ文字
環境
> sessionInfo() ## R version 3.4.3 (2017-11-30) ## Platform: x86_64-pc-linux-gnu (64-bit) ## Running under: Ubuntu 16.04.4 LTS ## ## Matrix products: default ## BLAS: /usr/lib/libblas/libblas.so.3.6.0 ## LAPACK: /usr/lib/lapack/liblapack.so.3.6.0 ## ## locale: ## [1] LC_CTYPE=ja_JP.UTF-8 LC_NUMERIC=C ## [3] LC_TIME=ja_JP.UTF-8 LC_COLLATE=ja_JP.UTF-8 ## [5] LC_MONETARY=ja_JP.UTF-8 LC_MESSAGES=ja_JP.UTF-8 ## [7] LC_PAPER=ja_JP.UTF-8 LC_NAME=C ## [9] LC_ADDRESS=C LC_TELEPHONE=C ## [11] LC_MEASUREMENT=ja_JP.UTF-8 LC_IDENTIFICATION=C ## ## attached base packages: ## [1] stats graphics grDevices utils datasets methods base ## ## other attached packages: ## [1] DiagrammeR_0.9.2 ## ## loaded via a namespace (and not attached): ## [1] Rcpp_0.12.15 pillar_1.1.0 compiler_3.4.3 ## [4] RColorBrewer_1.1-2 influenceR_0.1.0 plyr_1.8.4 ## [7] bindr_0.1 viridis_0.5.0 tools_3.4.3 ## [10] digest_0.6.15 jsonlite_1.5 viridisLite_0.3.0 ## [13] gtable_0.2.0 evaluate_0.10.1 tibble_1.4.2 ## [16] rgexf_0.15.3 pkgconfig_2.0.1 rlang_0.1.6 ## [19] igraph_1.1.2 rstudioapi_0.7 yaml_2.1.16 ## [22] bindrcpp_0.2 gridExtra_2.3 downloader_0.4 ## [25] dplyr_0.7.4 stringr_1.2.0 knitr_1.19 ## [28] htmlwidgets_1.0 hms_0.4.1 grid_3.4.3 ## [31] rprojroot_1.3-2 glue_1.2.0 R6_2.2.2 ## [34] Rook_1.1-1 XML_3.98-1.9 rmarkdown_1.9 ## [37] ggplot2_2.2.1 purrr_0.2.4 readr_1.1.1 ## [40] tidyr_0.8.0 magrittr_1.5 backports_1.1.2 ## [43] scales_0.5.0 htmltools_0.3.6 assertthat_0.2.0 ## [46] colorspace_1.3-2 brew_1.0-6 stringi_1.1.6 ## [49] visNetwork_2.0.3 lazyeval_0.2.1 munsell_0.4.3