使い方(つづき)

前回に引き続き、Graphvizの使い方を解説します。
このページではレイアウトは dot を前提とした説明になります。

自分でDOTファイルを書かずにサードパーティ製のツール経由でGraphvizを利用するだけという方は読み飛ばしてください。

サンプル4

ノードをグループ化したいときは、subgraph を使用します。
特殊なルールとして、サブグラフのグラフ名を “cluster” で始めると枠が自動的に付きます(penwidth=0で消すこともできます)。

// sample04_1.gv
digraph sample04_1 {
  subgraph cluster_0 {
    a -> b;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    x -> y -> z;
    label=“cluster 1”;
  }
  a -> x;
}
sample04_1

cluster_1 の内部を並列化(この場合横並び)してみます。

// sample04_2.gv
digraph sample04_2 {
  subgraph cluster_0 {
    a -> b;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    x -> y -> z;
    label=“cluster 1”;
    { rank=same; x y z } // x y z を並列化するにはこう書きます
  }
  a -> x;
}
sample04_2

次に、a と x (y,z) を同じ階層(ランク)に並べます。

// sample04_3.gv
digraph sample04_3 {
  subgraph cluster_0 {
    a -> b;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    x -> y -> z;
    label=“cluster 1”;
    { rank=same; x y z }
  }
  a -> x;
  { rank=same; a x } // a と x を並べる
}
a と x の並列化はできましたが、x,y,z の並列が崩れてしまいました。また、a, x がそれぞれのサブグラフから外れてしまっています。
どうやら、サブグラフの外でランクをいじると対象ノードがサブグラフから外れるようです。

sample04_3

サブグラフ間でノードのランクを調整するには newrank=true を追加します。

公式サイトから引用
If newrank=true, the ranking algorithm does a single global ranking

// sample04_4.gv
digraph sample04_4 {
  graph [ newrank=true; ]; // 追加
  subgraph cluster_0 {
    a -> b;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    x -> y -> z;
    label=“cluster 1”;
    { rank=same; x y z }
  }
  a -> x;
  { rank=same; a x }
}

これで意図通りになりました。

sample04_4

サンプル5

サブグラフ間、ノード・サブグラフ間の線を引くことも可能です。

// sample05_1.gv
digraph sample05_1 {
  compound=true;  // サブグラフに矢印を引くにはこれが必要
  subgraph cluster_0 {
    a -> b -> c;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    x -> y -> z [dir=none];
    label=“cluster 1”;
  }
  a -> x [ lhead=cluster_1,color=blue ]; // ノードからサブグラフ
  b -> y [ ltail=cluster_0,color=red ];  // サブグラフからノード
  c -> z [ lhead=cluster_1,ltail=cluster_0,color=darkgreen ]; // サブグラフからサブグラフ
}
sample05_1

サブグラフ同士を並列化したいときは次のようにします。

// sample05_2.gv
digraph sample05_2 {
  compound=true;
  newrank=true;  // これが必要!(サンプル4を参照)
  subgraph cluster_0 {
    a -> b -> c;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    x -> y -> z [dir=none];
    label=“cluster 1”;
  }
  a -> x [ lhead=cluster_1,color=blue ];
  b -> y [ ltail=cluster_0,color=red ];
  c -> z [ lhead=cluster_1,ltail=cluster_0,color=darkgreen ];
  { rank=same; a x } // 以下3行それぞれ並列化する
  { rank=same; b y }
  { rank=same; c z }
}
sample05_2

できましたが、矢印の違いが分かりにくくなったので、調整します。

// sample05_3.gv
digraph sample05_3 {
  compound=true;
  newrank=true;
  nodesep=0.7;  // ノード間隔調整
  subgraph cluster_0 {
    a -> b -> c;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    x -> y -> z [dir=none];
    label=“cluster 1”;
  }
  a -> x [ lhead=cluster_1,color=blue ];
  b -> y [ ltail=cluster_0,color=red ];
  c -> z [ lhead=cluster_1,ltail=cluster_0,color=darkgreen ];
  { rank=same; a x }
  { rank=same; b y }
  { rank=same; c z }
}

見やすくなりました。

sample05_3

サンプル6

階層の方向を変えることができます。

// sample06_1.gv
digraph sample06_1 {
  graph [ rankdir=LR; ]; // 追加
  subgraph cluster_0 {
    a -> b;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    x -> y -> z;
    label=“cluster 1”;
  }
  a -> x;
}
デフォルトの階層が上から下方向ですが、rankdir=LR を指定すると左から右へノードが延びます。

sample06_1

サンプル4と同様にクラスタ内の階層を揃えます(グラフの階層が左から右の順なので、この場合は縦並びにします)。

// sample06_2.gv
digraph sample06_2 {
  graph [ rankdir=LR; ];
  subgraph cluster_0 {
    a -> b;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    x -> y -> z;
    label=“cluster 1”;
    { rank=same; x y z } // 追加
  }
  a -> x;
}
階層は揃いましたが、x, y, z の上下が逆さまになってしまいました。

sample06_2
// sample06_3.gv
digraph sample06_3 {
  graph [ rankdir=LR ];
  subgraph cluster_0 {
    a -> b;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    edge [ constraint=false ]; // 追加
    x -> y -> z;
    label=“cluster 1”;
    { rank=same; x y z }
  }
  a -> x;
}

constraint=false を追加することで解消します(ノード構成によっては解消しない場合もあります)。

公式サイトから引用
If false, the edge is not used in ranking the nodes.

sample06_3

サンプル4と同様に a と x を同列にしてみます。

// sample06_4.gv
digraph sample06_4 {
  graph [ rankdir=LR; newrank=true; ];
  subgraph cluster_0 {
    a -> b;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    edge [ constraint=false ];
    x -> y -> z;
    label=“cluster 1”;
    { rank=same; x y z }
  }
  a -> x;
  { rank=same; a x } // 追加
}

a と x は並んだものの、サブグラフの上下が逆転してしまいました。

sample06_4

‘b -> x’ に見えない矢印(style=invis)を追加して並びをハックします。(難易度=高)

// sample06_5.gv
digraph sample06_5 {
  graph [ rankdir=LR; newrank=true; ];
  subgraph cluster_0 {
    a -> b;
    label=“cluster 0”;
  }
  subgraph cluster_1 {
    edge [ constraint=false ];
    x -> y -> z;
    label=“cluster 1”;
    { rank=same; x y z }
  }
  a -> x;
  { rank=same; a x }
  b -> x [ style=invis ]; // 追加
}

解消しました。

sample06_5

まとめ

サブグラフと階層の制御を中心に、基本的なことから裏技的なことまで見てきました。ノードの配置を細かくコントロールしようとするとなかなか難しいことがあるのも事実ですが、それほどこだわらなければ簡単に使えそうだということもお分かりいただけたのではないでしょうか。