たくさんの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が作れる。

graph

(例が二次関数のグラフなのは寂しいのでもうちょっと良い感じの絵を表示するようなものにしたい気持ちもあったりする)

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
}