背景
- jupyter notebookをデータ分析に使った際に、その結果をある程度再現可能なものにしたい
- 毎回頭から実行するのも面倒、またワンクリックで簡単に結果を見れるようにしたい
- notebookを書くことで、ダッシュボードを作る代わりにならないか
結論
- nbconvertで出来ます
- nbconvertをうまく使えば作成した画像だけ保存のようなこともできる
基本的には -
https://nbconvert.readthedocs.io/en/latest/nbconvert_library.html
を参照すればOK。 - コマンドラインからは
jupyter nbconvert --to html --execute test.ipynb
で一発で出来る.inplaceでやったりpdfにしたりとかもできる。
以下はソースコード上で実行したい、さらに細かい作業を行いたい場合の方法。
頭から末尾まで実行する
nbconvert.preprocessors
のExecutePreprocessor
を使えばできる。
import nbformat
from nbconvert.preprocessors import ExecutePreprocessor
input_name = "test.ipynb"
output_name = "out.ipynb"
with open(input_name) as f:
nb = nbformat.read(f, as_version=4)
ep = ExecutePreprocessor(timeout=500)
ep.preprocess(nb,resources={'metadata': {'path': '/'}})
with open(output_name, "w") as f:
nbformat.write(nb, f)
notebook をHTMLに変換
html_exporter = HTMLExporter()
html_exporter.template_name = 'classic'
body, resources = html_exporter.from_notebook_node(nb)
bodyに,HTMLが文字列として格納されるので、普通にファイルに保存してやればOK。
もう少し中身を見てみる(画像保存、notebookを書き換えて実行)
nbformatで読み込んだnotebookは辞書になっており、'cells'で、実際のセルにアクセスできる。
例えばこんなセルは
from datetime import datetime
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import load_iris
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
print(datetime.now())
以下のような感じで保存されている。sourceを適当に編集してやれば自動的な置換なども可能。(データセットのパスを変えたり、機密情報を入れたり隠したりには使えるかもしれない。)
{'cell_type': 'code',
'execution_count': 10,
'metadata': {},
'outputs': [{'name': 'stdout',
'output_type': 'stream',
'text': '2021-05-23 20:31:25.888013\n'}],
'source': 'from datetime import datetime\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd \nfrom sklearn.datasets import load_iris\n\niris = load_iris()\ndf = pd.DataFrame(iris.data, columns=iris.feature_names)\nprint(datetime.now())'}
画像はcellの中のoutputsなる配列に格納されている。BASE64でエンコードされているので読み込むには以下のような感じにする。(1番目のセルに画像が保存されているときの例。)
以下のようなセルには
plt.plot(np.random.rand(100))
以下のようにすれば画像をよみこんだりできる。
out = nb['cells'][1]['outputs'][1]['data']['image/png']
img = Image.open(io.BytesIO(base64.b64decode(out)))
RSTExporterを使うともう少し取り出しやすい形で画像を変換できる。
from nbconvert import RSTExporter
exporter = RSTExporter()
body, resources =exporter.from_notebook_node(nb)
img = Image.open(io.BytesIO(resources['outputs']['output_1_1.png']))