たくさんのnotebookの内容をコマンドラインから再実行して更新したい
はじめに
たとえばグラフを描画するnotebookをたくさん開いている状態。 ちょっとだけimportしたライブラリに変更があったので実行結果が変わりそうになってしまった。 このようなときに、すべてのipynbファイルを開いてrunallした後にsaveというようなことを手動で行いたくない。
はじめはheadless jupyterのようなものを作ろうとしたけれど。nbconvertで大丈夫だったという話。
nbconvert --execute
jupyterにはnbconvertというコマンドもついてくる。
これは、ipynbファイルを別のフォーマット(例えば、pdfだったりhtmlだったりmarkdownだったり)に変換するためのコマンドという認識だったのだけれど。
なんと--execute
というオプションがついている。
これを使うことで再実行が可能になる。こういう感じ。
$ jupyter nbconvert --to notebook --execute Untitled.ipynb
すると、 Untitled.nbconvert.ipynb
というファイルに再実行した結果が保存される。
--inplace
を使えば上書き保存できる
--inplace
を使えば上書き保存できる。こういう感じ。
$ jupyter nbconvert --to notebook --execute Untitled.ipynb
これは、Untitled.ipynbが更新される。
timeoutも伸ばしておいた方が良いかもしれない。
defaultでは30秒のtimeoutが設定されている。30秒以上掛かるような処理だった場合に中断されてしまうのは悲しい。そんなわけでtimeoutを伸ばしておくと良い。
$ jupyter nbconvert --ExecutePreprocessor.timeout=600 --to notebook --execute Untitled.ipynb
おまけ: 手で書いたipynbをレンダリングさせるということもできなくはない
こういう雑なmatplotlibで図を描くコードがあるとする。
import matplotlib.pyplot as plt plt.style.use("ggplot") xs = list(range(10)) ys = [x * x for x in xs] plt.plot(xs, ys, "g")
これを以下のようにipynbに手動で変換する(metadataなど埋めたほうが良い部分はあったりする)
{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "plt.style.use('ggplot')\n", "\n", "xs = list(range(10))\n", "ys = [x * x for x in xs]\n", "plt.plot(xs, ys, 'g')" ] } ], "metadata": {}, "nbformat": 4, "nbformat_minor": 2 }
.ipynbはJSONファイルなので手書き出来ないことはない。例えばgraph.ipynbなどの名前で保存しておく。
その後この保存したファイルに対してnbconvert --execute
を実行してグラフも含んだipynbを手に入れる事ができる。
(警告が出ているので本当はもう少し真面目にjson書いた方が良いかもしれない)
$ jupyter nbconvert --ExecutePreprocessor.timeout=600 --execute --to notebook 01graph.ipynb [NbConvertApp] Converting notebook 01graph.ipynb to notebook [NbConvertApp] ERROR | Notebook JSON is invalid: 'outputs' is a required property Failed validating 'required' in code_cell: On instance['cells'][0]: {'cell_type': 'code', 'execution_count': 1, 'metadata': {}, 'source': 'import matplotlib.pyplot as plt\n' "plt.style.use('ggplot')\n" '\n' 'xs = li...'} [NbConvertApp] Executing notebook with kernel: python [NbConvertApp] Writing 570 bytes to 01graph.nbconvert.ipynb
たくさん一気に同様の形式で別のファイルにグラフを描きたいときなどには便利かもしれない。
すると以下の様なグラフを生成した結果を含んだipynbが作れる。
(例が二次関数のグラフなのは寂しいのでもうちょっと良い感じの絵を表示するようなものにしたい気持ちもあったりする)
gistはこちら
追記: ipynbの作成にはnbformatを使うと便利
ipynbの作成にはJSONを手書きするよりnbformatを使うと便利。
例えば以下の様な形で使う。
import textwrap from nbformat.v4 import new_code_cell, new_notebook, writes_json notebook = new_notebook() sources = [ """ import random random.random() """, """ import random random.random() """, ] for i, source in enumerate(sources, 1): notebook["cells"].append(new_code_cell(textwrap.dedent(source), execution_count=i)) print(writes_json(notebook))
これを実行すると以下の様なipynbが生成される。便利。
{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "\n", "import random\n", "random.random()\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "\n", "import random\n", "random.random()\n" ] } ], "metadata": {}, "nbformat": 4, "nbformat_minor": 2 }
グラフ描く簡単な環境作っときたいかも
グラフ描くの簡単な環境作っときたいかも。要求は以下の2つ。
- 手軽にグラフが描ける
- 手軽にグラフを共有できる
前者は例えばjupyter上でだけ表示みたいなのが嫌な感じ。 後者は例えばGUIで表示とかだけなのは嫌な感じ。
まだ結局一番良いと思える方法は見つかっていない(かなしい)。
今のところの方法
今のところとりあえずグラフの描画にはpyplotを使うという気持ちでいる。
とりあえず表示する場合と共有する場合とで分けていて。全部jupyterで済ませれるならそれ。 そうじゃない場合は以下の方法でやっている。
画像を表示するだけ
import matplotlib.pyplot as plt plt.style.use('ggplot') plt.plot([1, 2, 3, 4]) plt.ylabel('y') plt.show()
画像を共有する場合
画像を出力してその画像を共有というのが無難な感じそう。
import matplotlib matplotlib.use("AGG") # NOQA import matplotlib.pyplot as plt plt.style.use('ggplot') plt.plot([1, 2, 3, 4]) plt.ylabel('y') dpi = float(plt.gcf().get_dpi()) plt.gcf().set_size_inches(400 / dpi, 300 / dpi) plt.savefig("images/line-400x300.png", dpi=dpi)
dpiの指定に現在のfigureが持っているdpiを使うのと、rcParamsの中のsavefigのdpiを使うのとどちらが良いのかあんまり分かっていない。
$identify images/line-400x300.png images/line-400x300.png PNG 400x300 400x300+0+0 8-bit sRGB 10.7KB 0.000u 0:00.000
ただ、gistにuploadするときにはちょっと大変な作業を踏まないといけないのであんまり画像で共有というのが機能しないような気もする。
問題点
問題点は以下の様な感じ。
- グラフの表示と画像として出力とでコードが変わってしまう
- 画像として出力してもgist上では楽に共有できない
画像として出力した結果を共有するなら画像サーバー的なものを用意してそこにuploadした後にmarkdown上で参照するという感じが良いのかも。 答えは見つかっていない。
jupyterでやれば良い場合
なんだかんだでgistで共有する場合にはjupyterでやるのが一番ラクな気もする。
import matplotlib.pyplot as plt plt.style.use('ggplot') plt.plot([1, 2, 3, 4]) plt.ylabel('some numbers')
以下をわすれずに
%matplotlib inline
問題点
問題点はipynbのレンダリングが遅いこと数秒程度は普通に待たされる。 その後
Sorry, something went wrong. Reload?
とか悲しい感じ(githubやgist上での話)。