カタカタブログ

SIerで働くITエンジニアがカタカタした記録を残す技術ブログ。Java, Oracle Database, Linuxが中心です。たまにRuby on Railsなども。

D3.js Force Layoutのパラメータについて調べてみた

前回、D3.jsのForce Layoutで簡単なネットワークを描いてみた。
D3.jsのForce Layout (力学モデルでグラフ描画するレイアウト)を動かしてみた - カタカタブログ

そのときは、Force Layoutの各種パラメータを全てデフォルトのまま使っていたが、実際は描画したいネットワーク図の特性に応じて、最適なものにチューニングできると望ましい。なので、パラメータの意味を調べつつ、動きを検証してみた。なお、パラメータの説明は公式サイトにもある。

公式サイト: https://github.com/mbostock/d3/wiki/Force-Layout

サンプルデータと標準の場合

テストデータは以下のように。前回よりはノード数を増やしている。

    var nodes = [ {id:0, label:"A"},
                  {id:1, label:"B"},
                  {id:2, label:"C"},
                  {id:3, label:"D"},
                  {id:4, label:"E"},
                  {id:5, label:"F"},
                  {id:6, label:"G"}
                ];
    var links = [ {source:0, target:1},
                  {source:0, target:2},
                  {source:0, target:3},
                  {source:3, target:4},
                  {source:3, target:5},
                  {source:3, target:6},
                  {source:6, target:6}
                ];

全てがデフォルト値の場合

まず、全てがデフォルトの場合を確認する。

   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .start(); 

f:id:osn_th:20141123114906p:plain

リンク距離

# force.linkDistance([distance])
デフォルト: 20

リンク距離はその名の通り、ノードとノードの間のリンクの長さを指定する。デフォルトは20と小さいので、10倍の200としてみた。

   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .linkDistance(200)
                 .start();

f:id:osn_th:20141123114901p:plain

リンク強度

# force.linkStrength([strength])
範囲: [0,1]
デフォルト: 1

よくわからないが、リンク間に及ぼす力のこと?値を小さくすると、リンク距離が短くなったように見える。

   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .linkDistance(200)
                 .linkStrength(0.1)
                 .start(); 

f:id:osn_th:20141123114859p:plain

摩擦力

# force.friction([friction])
範囲: [0,1] (0だと停止, 1以上は発散するため非推奨)
デフォルト: 0.9

公式サイトの説明によると、物理的な摩擦係数というよりは加速度の減衰力のイメージらしい。つまり値を小さくすると収束が早くなり、値を1以上にすると発散してしまうよう。

0.1だとアニメーションの収束が早くてすぐに停止する

   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .linkDistance(200)
                 .friction(0.1)
                 .start();

f:id:osn_th:20141123114904p:plain

推進力(反発力)

# force.charge([charge])
デフォルト: -30

ノード同士の間に働く力のこと。負の値だとノード同士が離れようとする反発力に、正の値だと引き合う力になるよう。

 -500だと大きく反発する

   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .linkDistance(200)
                 .charge(-500)
                 .start(); 

f:id:osn_th:20141123114856p:plain
30だと引きあう

   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .linkDistance(200)
                 .charge(30)
                 .start(); 

f:id:osn_th:20141123114909p:plain

chargeDistance (よくわからない。。)

# force.chargeDistance([distance])
デフォルト: 無限

よくわからないが、動きだけ確認。

0だとリンクが重なる

   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .linkDistance(200)
                 .chargeDistance(0)
                 .start(); 

f:id:osn_th:20141123114913p:plain
100だとやや離れる。

   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .linkDistance(200)
                 .chargeDistance(100)
                 .start(); 

f:id:osn_th:20141123114917p:plain

theta

# force.theta([theta])
デフォルト: 0.8

通常、全てのノード同士がお互いに力を及ぼし合ってノード同士の位置が決まるが、全ノード同士で計算するとコストが多くなる。そのため、計算を省略するためのしきい値を設定し、ある程度距離が近いノード同士はそれをひとかたまりのノードとみなして計算するようにする。これはそのしきい値を指定する。
※大規模なグラフに対してだと有効そう。検証は省略。

重力

# force.gravity([gravity])
デフォルト: 0.1
重力は画面の中心に働く力。

重力10倍

   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .linkDistance(200)
                 .gravity(1)
                 .start(); 

f:id:osn_th:20141123114915p:plain
重力0

   var force = d3.layout.force()
                 .nodes(nodes)
                 .links(links)
                 .size([w, h])
                 .linkDistance(200)
                 .gravity(0)
                 .start(); 

f:id:osn_th:20141123114911p:plain

まとめ

パラメータをいろいろいじるとネットワークレイアウトが大きく変わることがわかった。
本来はパラメータは固定値ではなく、関数にしてリンク数とか、ネットワーク特性に応じて動的に変えられるようにするとよりよいのかもしれない。

以上。