TL; DR

背景:Jupyterの使い方は意外とハイコンテクスト
Pythonを分析に使っている方で数値分析のコードをなるべくさっさとエンドユーザーに提供したいけど、UIまで作るのが面倒くさいという方は多いのでは。
私は製造業で働いているのでユーザーがコマンドラインの操作に慣れてもいないことも多く、ユーザー側の環境構築を考えるのも手間。サーバー上のJupyter Notebookをそのまま提供すると「これ何?」となる。
というわけでユーザー配布用のJupyterを考えてみました。これが結構好評だったので、Jupyter Notebook Advent Calender 2019 14日目として紹介します。アドカレに参加するのは初めてなので補足頂けることがあればぜひコメントください。
方法:jupyter_contrib_nbextensionsとipywidgets
準備
jupyter_contrib_nbextensionsはJupyterに様々な追加機能を与えるものです。extentionsは使わなくてもこのノートは作れるのですが、格段に楽になるので積極的に利用します。
ipywidgetsはNotebook上で入力のためのUIを作るためのものです。これを使ってユーザーからの入力を受け付けて変数に格納できます。

ipywidgets
ipywidgetsの使い方は多くの記事が書かれているのでここでは省略しますが、上のgifの例では以下のようなコードを書いています。
まず必要なライブラリのimport。
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import ipywidgets as widgets
from IPython.display import Javascript
次に真下のセルを実行する関数を作っておきます。
Notebook上でJavascriptを実行することで実現させます。
def run_under_cell(ev):
display(Javascript('IPython.notebook.execute_cell_range(\
IPython.notebook.get_selected_index()+1,\
IPython.notebook.get_selected_index()+2)'))
ipywidgetsの名前空間に様々な入力インターフェースが用意されています。沢山あるので調べながら使いましょう。
ボタンのインターフェースを使って先程作った真下のセル実行関数をコールバックして、下に続くロジックのコードを叩きます。多段階の工程の場合順にボタンを押していくことになります。
title = widgets.Text(description="タイトル")
slope = widgets.BoundedFloatText(1, description="傾き")
intercept = widgets.FloatSlider(description="切片")
button = widgets.Button(description="入力後にクリック")
button.on_click(run_under_cell)
user_input = widgets.HBox([title, slope, intercept])
user_input = widgets.VBox([user_input, button])
display(user_input)
最後にユーザーからの入力に基づいて処理を行うコードを書いていきます。
以下は例です。
x = np.arange(0, 11)
y = slope.value * x + intercept.value
plt.plot(x, y)
plt.title(title.value);
私はユーザーの誤操作を防ぎ、見た目をスッキリさせたいのでタイトルバーやツールバーを消しています。お好みでどうぞ。
%%javascript
$('#header-container').toggle(false);
$('#maintoolbar.navbar').toggle(false);
jupyter_contrib_nbextentions
書いたコードのセルの設定を変更してやります。
やることは3つです。
-
- Markdownのセルを選択不可にする
Markdownのセルを選んでから雪のアイコンをクリックする
セルをダブルクリックしても反応しなくなる
importのセル、UI表示のセル、Javascriptのマジックセルなど、ユーザーの入力を待たないセルをNotebookを開いときに自動実行する
View→Cell Toolbar→Initialization Cellsを選択
入力を待たないセルの右側に出るInitialization cellのチェックボックスをクリックする
Markdown以外のセルを非表示にする
コードの書かれたセルを選択して[^]ボタンで隠蔽する
以上で完成です!
良いところと悪いところ
この方法の良いところとして、結構面倒なUIを作る作業がめちゃくちゃ楽です。というか、Jupyter上でコードが完成すればほぼ終わりです。サーバーに置いておけばユーザー側で環境設定の必要もなし。Jupyterの操作説明の必要もなし。この条件で計算して、この条件で可視化して…という処理を簡単に提供できます。
なによりMarkdownのセルにガシガシ説明を書いておけるのが本当に気に入ってます。
ハイパーパラメーターやp値だけユーザーに選んでもらって分析するときなども、この変数が大体何を意味するのか書いておけば何も伝えなくてもユーザーの側で色々試してくれてます。
一方悪いところとして、コードが完全に隠蔽されているわけではないので渡す相手を選びます。
ページコンポーネント間を行ったり来たるするようなやり取りが出来ないので直列な処理しか出来ません。探索的な分析には向いていないように思います。
これをソフト的運用と見なすと、Jupyterは実行しただけで書き換わるのでGitの相性が良くない気がします。何かいい方法あるのかな。
またサーバー上のノートブックの場合複数人が同時に使用すると意図したものと異なる結果になります。
それでも簡単な分析を提供する場合はNotebookにちょっと工夫を入れるだけで使いやすい!という印象をもたせることが出来ます。一手間加えてイケてるNotebookを作りましょう!