読者です 読者をやめる 読者になる 読者になる

Λlisue's blog

つれづれなるままに更新されないブログ

Jupyter (旧 IPython notebook)の本来あるべき姿を垣間見る

どうも、ご無沙汰しております Pythonista + Vimmer、略して Pythonimmer な有末です。 この記事は Python その2 Advent Calendar 2015 22 日目の記事となります。

Jupyter とは

さて、皆様は Jupyter(旧 IPython notebook) というプロジェクトをご存知でしょうか? Jupyterがすごい勢いでやってくるからお前ら備えとけ(IPython Notebook + R)Railsエンジニアに役立つJupyter NotebookとiRuby に代表されるように、現在各界から注目されているプロジェクトとなっております

簡単に言うと Web ブラウザ上にて Markdown および任意の言語の記述・実行・表示を行うサービス となります。 科学者が使う実験ノートにインスパイアされたプロジェクトであることから、主に実験結果の解析スクリプトなどで真価を発揮し、以下の様な利点があります。

  • 一連の解析の各ステップを一つのファイルのセクション単位で記載することで、後日確認した時に流れがわかりやすい
  • 解析の過程で必ず存在する「思考過程」や「結果・考察」などを Markdown にて記載可能なので、ソースコードでは表現が難しい概念なども画像などのリソースを用いてわかりやすくまとめることができる
  • matplotlib に代表されるグラフ描画ライブラリのインライン展開に対応しており、ソースコードの実行結果を同じ画面にて確認可能(下記スクリーンショット参照)

通常のプログラムではなく、一過的なスクリプトを書く場合は非常に便利なソフトウェアとなっております。 Try Jupyter にて簡単に試すことが出来るので、ご存じなかった方は一度お試しください。

f:id:lambdalisue:20151222211842p:plain

Jupyter にて Python を実行し解析結果をグラフで表示している様子

Jupyter の辛いところ

このように、研究者の研究者による研究者のためのソフトウェアである Jupyter ですが、研究者の卵である僕は暫くの間利用しておりませんでした。 なぜかって?

テキスト書くのに Vim が使えないとかストレスしかない

じゃないですか。それもソースコードを記載するのに

  • j, k, h, l 移動ができない
  • Motion が使えない
  • 行編集ができない (yy, dd, ...)
  • 矩形選択が使えない

など、様々な制約があり

気が付いたら o, i, a, j, k, h, l, :w, ... の嵐

ストレスで解析どころじゃないですね。

もちろん、Vimテキストエディタの最終形 なので、OSSプロジェクトであるソフトウェアには大抵の場合 Vimキーバインドというのが用意されております。 Jupyter の前身である IPython notebbok でも

などが存在しているのですが IPython notebook の進化系である Jupyter では全く動きませんでした。 とても悔しく、いろいろ解析してみたのですが、どうも上記の2つは共に CodeMirror 標準の Vim 機能を有効化する ことで Vim バインディングを提供しているようでした。 そのため Jupyter の API に合わせて書きなおしてあげることで上記の技が使えそうだという光が見えてきました。

パンがないならケーキを食べればいいじゃない

上述した CodeMirror の Vim バインディングJavaScript による(実用性がある) Vim 実装の中ではトップクラスのものです。 テキストオブジェクトを初めとする様々なモーションやオペレータ、ビジュアルモードや矩形選択、カーソルジャンプやキーバインディングのカスタマイズにも対応と至れり尽くせりな実装となっており、これを完全に有効にしてしまえば、Jupyter の辛いところがほぼ解決することが示唆されていました。

ここまでわかってれば、やることは一つ。

パン(既存)がないならケーキ(新規)を食べればいいじゃない

ということで jupyter-vim-binding というプラグインを作成しました。

github.com

調査も含め、このプラグインを完成させるにはある程度時間がかかりましたが、これによりJupyter を利用した解析効率が飛躍的に向上しました ;-)

jupyter-vim-binding の利用

上記リンクの README にインストール方法が書いてありますが、念のためこちらでも「Jupyter 上の Python を利用した方法」を解説します。 gitリポジトリを追従する場合や jupyter コマンドを利用したい場合はリンク先を参照してください。

Jupyter を開きインタプリタPython を選択して、以下のコードを実行する

from notebook.nbextensions import install_nbextension
from jupyter_core.paths import jupyter_data_dir
install_nbextension('https://rawgithub.com/lambdalisue/jupyter-vim-binding/master/vim_binding.js',
                        nbextensions_dir=jupyter_data_dir()+'/nbextensions/vim_binding')

一時的に有効 にする場合は同 Jupyter 上で以下のコードを実行

%%javascript
Jupyter.utils.load_extensions('vim_binding/vim_binding')

永続的に有効 にする場合は同 Jupyter 上で以下のコードを実行

%%javascript
Jupyter.notebook.config.update({
    'load_extensions': { 'vim_binding': true },
});

上記に従ったあと Jupyter 自体を再起動してください(Jupyter は起動時に存在しないファイルをロードしない)。 その後セルをクリックすると下図の Command Mode のように背景が淡い黄色になり、カーソルが緑色の大きなカーソルに変化します。これが Vim のコマンドモードになります。ここから更に i を押すことで Insert Mode に以降し文字入力が可能になります(この辺りは Vim ですね)。

f:id:lambdalisue:20151222231446p:plain

Vim との大きな違いは Jupyter Mode というものが存在していることです。ここで Jupyter 特有の様々なコマンド(例: インタプリタの再起動)やセルの移動が可能となっております(セルの移動は Command Mode にて Ctrl-i/j でも可能)。 詳細は Jupyter Mode にて <S-h> を押すこと(もしくは Help > Keyboard Shortcut メニュー)で表示されるキーバインディングのヘルプを御覧ください。

jupyter-vim-binding の利点

ipython-vimception が利用していたレガシーなプラグインの仕組みは将来的に非推奨になる可能性が高かったため、本体のコードや新し目のプラグイン実装を参考に require.js ベースの方法にて作成しました(あまりドキュメントが整備されていない部分だったので、この辺の仕組みは変わる可能性はありますが……)。 これにより JavaScript だけですべての機能を提供することができ、インストールが簡単になりました。 非プログラマでも Jupyter 上だけでインストールすることが出来ます。

また、Jupyter などの非プログラマが利用するソフトウェアのプラグイン(例: ImageJ のプラグイン全部)は「動けばOK」な思想で組まれて行儀が悪いコードが多いですが、一応プログラマ歴10年を超えているので「お行儀」にも気をつけております ;-)

加えて Jupyter では Vimmer には辛いキーバインディングが用意されているので、この辺りを Vim 的なバインディングに置き換えております。 普段から Vim を利用している皆様ならば <S-h>H)で表示されるキーバインディングのヘルプを数秒眺めるだけで使い方がわかると思います。

おわりに

個人的には Life changing なプラグインだと思っております。 また CodeMirror は Vim 以外にも EmacsSublime のキーバインディングも持っているため jupyter-vim-binding をフォークして Vim 固有部分を書きなおすことで EmacsSublime 用のプラグインも作成できると思います。 Jupyter はもうすぐ IPython notebook から完全に分離した Version 1.0 が出そう……という今がまさに熱い時期なので Emacs ユーザーや Sublime ユーザーの方は是非チャレンジして世界に名前を売りましょう!(笑。