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();
リンク距離
# force.linkDistance([distance])
デフォルト: 20
リンク距離はその名の通り、ノードとノードの間のリンクの長さを指定する。デフォルトは20と小さいので、10倍の200としてみた。
var force = d3.layout.force() .nodes(nodes) .links(links) .size([w, h]) .linkDistance(200) .start();
リンク強度
# force.linkStrength([strength])
範囲: [0,1]
デフォルト: 1
よくわからないが、リンク間に及ぼす力のこと?値を小さくすると、リンク距離が短くなったように見える。
var force = d3.layout.force() .nodes(nodes) .links(links) .size([w, h]) .linkDistance(200) .linkStrength(0.1) .start();
摩擦力
# 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();
推進力(反発力)
# force.charge([charge])
デフォルト: -30
ノード同士の間に働く力のこと。負の値だとノード同士が離れようとする反発力に、正の値だと引き合う力になるよう。
-500だと大きく反発する
var force = d3.layout.force() .nodes(nodes) .links(links) .size([w, h]) .linkDistance(200) .charge(-500) .start();
30だと引きあう
var force = d3.layout.force() .nodes(nodes) .links(links) .size([w, h]) .linkDistance(200) .charge(30) .start();
chargeDistance (よくわからない。。)
# force.chargeDistance([distance])
デフォルト: 無限
よくわからないが、動きだけ確認。
0だとリンクが重なる
var force = d3.layout.force() .nodes(nodes) .links(links) .size([w, h]) .linkDistance(200) .chargeDistance(0) .start();
100だとやや離れる。
var force = d3.layout.force() .nodes(nodes) .links(links) .size([w, h]) .linkDistance(200) .chargeDistance(100) .start();
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();
重力0
var force = d3.layout.force() .nodes(nodes) .links(links) .size([w, h]) .linkDistance(200) .gravity(0) .start();
まとめ
パラメータをいろいろいじるとネットワークレイアウトが大きく変わることがわかった。
本来はパラメータは固定値ではなく、関数にしてリンク数とか、ネットワーク特性に応じて動的に変えられるようにするとよりよいのかもしれない。
以上。