LoginSignup
10
10

More than 3 years have passed since last update.

Jupyter NotebookでのD3.jsによる木構造の描画

Last updated at Posted at 2019-11-27

やりたいこと

木構造データをJupyter Notebookとか、ウェブアプリとかで美しく描画したい!というのがモチベーションです。Pythonを使うのは元データを分析する都合からで、インタラクティブに分析と可視化を繰り返したいと考えたからです。

Python

Pythonで木構造を描画するやり方はいろいろあるようでして、一番最初に思いつくのは、networkxgraphvizとかのグラフライブラリを使うことだけど、そもそもがグラフ構造の分析を行うことが目的のライブラリなので、凝った表現は難しいと考えます。

一方で、木構造の専用の分析・可視化ツールとしては、ETE Toolkitがあります。木構造の表現としてはよく知られているらしいThe Newick formatを使ってモデルを表現します。これもなかなか良さそうですが、ウェブでの親和性を考慮すると、モデルの記述にはJSONなどのもう少し汎用性の高いものを使いたいなと考えました。

Javascript

Javascriptでのグラフ構造の描画については、ここに良いまとめがあります。いろいろありますが、汎用性や情報量の多さで言うとやはり、D3.jsかなと思います。木構造(tree)の描画についてもライブラリ化されています。

というわけで、Jupyter Notebookを使ったD3.jsでの木構造の描画を試みます。Jupyter Notebookというのは、セルマジック(%%javascript)を使うことで、Pythonカーネルを使ったままで、Javascriptの実行やHTMLの描画をすることができるので、もともと親和性は良いのです。

Jupyter NotebookでのD3.jsの利用

やり方としては、Javascriptのセルマジックを使ってやるのと、py_d3を使ってやるのが考えられます。前者はまぁ、RequireJSとかを使って、直感的には分かりにくい記述になります(ここを参照)。後者は、Javascriptのセルマジックをいい感じにラップしているようです。ここではpy_d3を使った描画の解説をします。

準備

何はともあれ、py_d3のインストールをします。pipを使いましょう。pip install py_d3
最初に、以下のようにモジュールを読み込みます。

import py_d3
py_d3.load_ipython_extension(get_ipython())

以下はホームページのサンプルの引用です。最初の%%d3 5.12.0でd3.jsのバージョン指定をしています。どのバージョンが使えるかは、%d3 versionとコマンドを打てば分かります。


%%d3 5.12.0

<g></g>

<style>
element {
    height: 25px;
}
div.bar {
    display: inline-block;
    width: 20px;
    height: 75px;
    margin-right: 2px;
    background-color: teal;
}
</style>

<script>
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
                11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];

d3.select("g").selectAll("div")
    .data(dataset)
    .enter()
    .append("div")
    .attr("class", "bar")
    .style("height", function(d) {
        var barHeight = d * 5;
        return barHeight + "px";
    });
</script>

以下のようなグラフの出力が確認できます。
image.png

Pythonとのデータ連携

RequireJSを駆使すれば、定義したJavascriptの関数に、Pythonで計算したオブジェクトや変数を入力することは可能です。ただ、JSONで出力してd3.jsで読み込むのと大差ないので、あまり巨大なデータでなければ、中間ファイルを経由するのが簡単です。例えば、以下のように木構造のJSONファイルを準備します。

import json
data = {
    "name": "A",
    "children": [
      { "name": "B" },
      {
        "name": "C",
        "children": [{ "name": "D" }, { "name": "E" }, { "name": "F" }]
      },
      { "name": "G" },
      {
        "name": "H",
        "children": [{ "name": "I" }, { "name": "J" }]
      },
      { "name": "K" }
    ]
  };
json_file = open('test.json', 'w')
json.dump(data, json_file)

木構造の描画

木構造の描画には、d3 hierarchyのツリーレイアウトを使います。JSONデータ構造をロードして、そのデータをもとにtreeを作ったら、あとはSVGの描画の調整をするだけです。描画の細かい調整については、d3.jsやSVGのドキュメントを参照ください。

%%d3 5.12.0

<style>
.link {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5px;
}
</style>

<svg width="800" height="600"></svg>

<script>
var width = 800;
var height = 600;

var g = d3.select("svg").append("g")
    .attr("transform", "translate(80,0)");
     console.log("data");


d3.json("test.json")
    .then((data) => {
        console.log(data);
        var root = d3.hierarchy(data);
        var tree = d3.tree(root).size([height, width - 160]);
        tree(root);

        var link = g.selectAll(".link")
            .data(root.descendants().slice(1))
            .enter()
            .append("path")
            .attr("class", "link")
            .attr("d", (d) => {
                return "M" + d.y + "," + d.x +
                    "C" + (d.parent.y + 100) + "," + d.x +
                    " " + (d.parent.y + 100) + "," + d.parent.x +
                    " " + d.parent.y + "," + d.parent.x;
            });

        var node = g.selectAll(".node")
            .data(root.descendants())
            .enter()
            .append("g")
            .attr("class", "node")
            .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

        node.append("circle")
            .attr("r", 8)
            .attr("fill", "#999");

        node.append("text")
            .attr("dy", 3)
            .attr("x", function(d) { return d.children ? -12 : 12; })
            .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
            .attr("font-size", "200%")
            .text(function(d) { return d.data.name; });
        })
   .catch((error)=>{

   }
)
</script>

image.png

参考

  1. ネットワークグラフ描画ライブラリ7個まとめ
  2. D3.js v4/v5 tree 使い方 – サンプル
  3. Jupyter Notebook x d3.jsでリッチなデータビジュアライゼーション
  4. Custom D3.js Visualization in a Jupyter Notebook
10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10