Jupyter notebook って便利ですよね。私も数式の入ったメモを入れたい場合や、コードとプロット結果を一緒にしたいときにはよく利用しています*1。
ただ一つ問題があって git との相性が恐ろしいほどに悪いことで有名ですよね。たった1行コードを追加しただけでも notebook の内部はそれ以上に変化してしまうので、git で管理しようにもどこに変更が入ったのか全然わかりません。
折衷案として notebook をソースコードに変換してそれを git で管理すると言う方法があります。
notebook のままだと1行追加するだけでも jupyter を起動せねばならず私としては億劫なのですが*2、ソースコードなら編集も気軽にすることができます。しかし、nbconvert
では notebook --> ソースコードの変換はできても、ソースコード --> notebook の変換ができないことが悩みの種でした。
そんなところに彗星のごとく現れた救世主が今回紹介する Literate.jl です。これを使うことで Julia のソースコードから notebook を生成することができます。README を読んだ時はいまいちイメージできなかったのですが、実際に notebook が作成されると感動しました!
以下、$
で始まるコードブロックは bash での実行を、julia>
で始まるコードブロックは Julia の REPL での実行を表します。
目次
環境
julia> versioninfo() Julia Version 1.0.0 Commit 5d4eaca0c9 (2018-08-08 20:58 UTC) Platform Info: OS: Linux (x86_64-pc-linux-gnu) CPU: Intel(R) Xeon(R) CPU E5645 @ 2.40GHz WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-6.0.0 (ORCJIT, westmere) Environment: JULIA_SHELL = /usr/bin/zsh
インストール
Literate.jl をインストール
julia> using Pkg; Pkg.add("Literate")
notebook からソースコードを生成する時に jupyter_contrib_nbextensions も必要らしいので入れます。
$ conda install -c conda-forge jupyter_contrib_nbextensions
使い方
Julia のソースコードを notebook に変換
まずはサンプルとして以下の内容を example.jl
として作ってください。
# # Hello World # こんにちは x = 1 println("Hello World!")
ソースコード上でコメントになっているところが markdown cell になり、コードの部分が code cell になります。
Julia を起動し、以下のコマンドを実行してみましょう。
julia> using Literate julia> Literate.notebook("example.jl", "."; documenter=false, credit=false, execute=false)
すると、example.ipynb
という notebook がカレントディレクトリにできるはずです。
中身を見てみましょう!
どうですか!ちゃんと notebook が生成されましたよ!!!
先程は execute
を false
にしましたが、ここを true
にすると実行までされた notebook が生成されます。
julia> Literate.notebook("example.jl", "."; documenter=false, credit=false, execute=true)
#-
を入れると cell を区切ることが出来ます。
example.jl
# # Hello World # こんにちは x = 1 #- println("Hello World!")
Julia のソースコード を markdown に変換
LIterate 使うと Julia のソースコードから markdown の生成も出来ます。
Literate.markdown("example.jl", "."; documenter=false, credit=false)
notebook を Julia のソースコードに変換
心を新たにし、example.jl
はまだ無いとします。
notebook から Julia のソースコードへの変換は "Download as" で Julia を選べばOK♪...では無いんです。残念。。。
実際ダウンロードしてみればわかりますが、下記のように markdown の内容が消失します。
x = 1 println("Hello World!")
Python をダウンロードする時はちゃんと markdown も残ってるんですけど、Julia の場合だと消失します。
IJulia にあった issue によると nbconvert
の問題だそうです。
うまいことテンプレートを作ると良いらしいのですが、ドキュメントを読んでもよくわからなかったので、上のリンク中で Carreau さんが提示されているテンプレートを使わせていただきました。
Customizing nbconvert — nbconvert 5.3.1 documentation
julia.tpl
を新規作成
{%- extends 'script.tpl' -%} {% block markdowncell scoped %} {{ cell.source | comment_lines(prefix='# ') }} {% endblock markdowncell %}
notebook を Julia のソースコードに変換
$ jupyter nbconvert --to script 'example.ipynb' --template=julia.tpl
すると example.jl
というファイルが出来ます。中身はこんな感じ。
# # Hello World # こんにちは x = 1 println("Hello World!")
変換されたこの書式はまさに Literate.jl で notebook に変換できる書式そのものではありませんか!!!
問題点
notebook --> ソースコード、ソースコード --> notebook と相互変換が出来るようになりましたが、完全に可逆というわけではありません。
ソースコード --> notebook の変換の時 #-
を入れると cell を分割することが出来ましたが、notebook --> ソースコード では分割を表すような記号が入らないので notebook --> ソースコード --> notebook と変換をすると cell の分割が消えます。テンプレートで回避できそうな気がしますが、私は Jupyter の構造に詳しくないので解決策はわかりません。
おわりに
Literate で変換するソースコードを git で管理すれば、間接的に notebook を git で管理していることになるので大分 notebook のバージョン管理がやりやすくなるのではと思います。
Python だと gitnb というのを使うと notebook を git で管理しやすいようです。
GitHub - brookisme/gitnb: git tracking for python notebooks