jupyter notebookでpythonとJavaScriptの連携


はじめに

jupyter notebookでpythonだけでもデータ解析から可視化まで一連の流れが可能ですが、D3.jsの高い表現力とインタラクティブ性をみてしまうと、可視化はJavaScriptでというケースも多くなるのではないでしょうか。そこで、データはpythonで用意してJavaScriptに引き渡す方法を紹介したいと思います。

以下の説明ではpy_d3というjupyter notebook上でD3.jsのコードを実行、表示するパッケージを利用しますが、D3.jsに限らず任意のJavaScriptが実行可能です。1

py_d3のセットアップ

pip install py_d3

でインストールできます。機能を利用するにはnotebookの先頭などで以下のように記述します。

%load_ext py_d3

その上でセルの先頭で %%d3と記述するとそのセル中のコードはHTMLとJavaScriptとして解釈されます。

詳しくはこちらの記事などを参考にしてください。 2

前準備

連携はIPython.display.HTMLパッケージの機能を使いますがそのままでは使いにくいので、以下のJavaScriptの関数を定義してラッピングして使うことにします。%load_ext py_d3の直後のセルなどで実行してください。snippetsなどに登録しておくのがいいです。

%%d3
<script>
function pyexec(command) {
    return new Promise(res => {
        IPython.notebook.kernel.execute(command,
            {iopub: {
                output: 
                    out => res(JSON.parse(eval(out.content.data["text/plain"])))
            }}, 
            {silent: false});
    });
}
</script>

連携

実行するpythonのコードを文字列にして上記のpyexec関数に渡します。python側ではjson形式で値をもどすようにしてください。

たとえばpython側で以下のように関数を定義しておきます。

import json
import numpy as np
def get_data(count):
    return json.dumps((np.random.rand(count)*10).tolist())

JavaScript側は以下のように記述します。pyexecのあとのthenの中でデータを受け取ってから行う処理を記述します。今回はD3.jsのチュートリアルにのっていたシンプルなサンプルを実行しています。

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

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

すると以下のようなグラフがnotebook上に描画されます。

今回作成したnotebookを公開しておきます→ http://nbviewer.jupyter.org/gist/ssugiyama/29b586b25dc63730eb67ee6c1daefac8

参考

脚注


  1. py_d3を使わず標準のIPython.display.HTMLパッケージだけでも連携可能ですが、JavaScriptとHTMLのコードを文字列の中に書く必要があり、エスケープなどが面倒なので、セルの中に直接JavaScriptとHTMLが書けるpy_d3を推奨します。 

  2. ただし、py_d3のロードの方法はこの記事の記述から変更されており %load_ext py_d3を使うようにしてください。