あなたの生産性を向上させるJupyter notebook Tips
河野晋策
このエントリは全9回を予定する18卒新人ブログリレーの第3回です.
はじめまして.今年度よりリクルートテクノロジーズに入社した河野 晋策です.
7月からQassチームにて検索ロジックの改善を行っています.
Qassチームは,検索基盤の運用や検索ロジックの改善を行っているチームです.
詳しくは以下の記事をご覧ください.
本記事の想定読者:普段Jupyter notebook・Jupyter Lab,Google Colaboratoryを使っている方,またこれから使おうと考えている方
本記事の概要:jupyter notebookの知見共有
はじめに
Jupyter notebookとは
近年,データの重要性が様々なところで説かれており,リクルートテクノロジーズでもデータ活用の取り組みが多く行われています[1, 2].
また,Jupyterを使用する人や,会社も年々増えています[3, 4].
Jupyterは,Pythonをはじめ,R,Julia,Ruby,Cなど40を越える言語[6]のREPLとして活用できるオープンソースのウェブアプリケーションです.
またnotebookは,プログラムとその実行結果(図や表などを含む),リッチテキストを含むデータ分析に特化した実行可能なドキュメントです.
弊チームでは多くのデータ活用事例で,このJupyter notebookを使用しており,分析から結果の共有まで広く利用しています.
検索組織である弊チームでは,リクルートの多種多様なサービスにおいて,クライアントとカスタマーの両者にとって最適な検索を実現すること目指しています.
そのために,ユーザが「どのようなコンテンツに興味を示したか」,「どのようなコンテンツにコンバージョンしたか」,また,「コンバージョンに至る導線はどうだったか」,「検索によって表示されたコンテンツの中で選ばれたのはどれだったか」などユーザの行動を分析しています.
このデータ分析によって得られた事実・仮説をもとに既存の検索アルゴリズムをチューニング,あるいは新規の検索アルゴリズムを開発し,日々検索のパフォーマンスを向上させています.
これらの業務にJupyter notebookを用いることで,分析がスムーズになり,さらに分析結果の共有を簡単に行うことができています.
また,最近では,Googleが提供しているColaboratoryを使用することで環境構築の手間なく簡単にデータ分析環境を手に入れることもでき,使用者が増えています.
本記事の内容
前述のようにJupyter notebookの使用者は増えていますが,ユーザがJupyter notebookを最大限活用できていないことを示唆する報告もあります[7, 8].
そこで本記事では,普段Jupyterを使っている方,また導入を考えている方を対象にJupyterを使った作業の生産性を向上するような業務内外で得た知見を提供できればと思います.
本稿の構成は以下の通りです.
1 notebookにおけるコードの再利用
Jupyter notebookにおいて,新しいnotebookを立ち上げた時,以前のnotebookからコードを再利用することが多々あるのではないでしょうか?
文献[7, 8]では,再利用のためのnotebookを作成しているユーザもいることが報告されています.
例えば以下のようなインポート文です.
1 2 3 4 5 6 7 8 9 |
import numpy as np import pandas as pd import matplotlib.pyplot as plt %matplotlib inline from tqdm import tqdm_notebook as tqdm from time import sleep from collections import Counter |
分析のための時間を,コードを探してコピー&ペーストする時間に割くのは,非常に勿体無いと言えるでしょう.
この問題を解決する方法は主に2つあります.
1.1 Nbextensions[9]
1つ目は,jupyter_contrib_nbextensionsのJupyter notebook snippets menuを使う方法です.
これにより,notebookのナビバーから登録済みのスニペットを瞬時に貼りつけることが可能です.
1.1.1 Jupyter notebook snippets menuの使い方
jupyter_contrib_nbextensionsのインストールは以下で行います.
1 2 3 |
$ conda install -c conda-forge jupyter_contrib_nbextensions $ jupyter contrib nbextension install --user |
上記コードが正常終了し,jupyter_contrib_nbextensionsのインストールが完了すれば,Nbextensionsを使用することができます.
Jupyter notebookを再起動し, http://localhost:8888/tree#nbextensions_configurator
にアクセスすれば,Nbextensionsの設定タブにアクセスできます.
まず,Snippets Menuの設定メニューで Include custom menu content parsed from JSON string below
のボックスにチェックします.
次に JSON string parsed to define custom menus (only used if the option above is checked)
にスニペットとして登録しておくコードをJSON形式で入力します.
上記Gifでは以下のスニペットを入力しています.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
{ "name": "My favorites", "sub-menu": [ { "name": "いつもの", "snippet": [ "import numpy as np", "import pandas as pd", "import matplotlib.pyplot as plt", "%matplotlib inline", " ", "from tqdm import tqdm_notebook as tqdm", "from time import sleep", "from collections import Counter" ] } ] } |
1.2 IPythonのstartup機能
2つ目の方法は,IPythonのstartup機能 を使う方法です.
IPythonのstartup機能を使用することでnotebookの起動時に任意のPythonコードを実行することができます.
以下のGifを見るとnotebook起動時に numpy
と matlotlib
を自動的にロードできているのがわかると思います.
1.2.1 startup機能使い方
startup機能を利用するためには,まず,ipythonコマンドでプロファイルを作成する必要があります.
notebook上から以下のコードを実行します.
1 2 3 |
!ipython profile create !touch ~/.ipython/profile_default/startup/00-import_list.ipy |
00-import_list.ipy
に書いたコードはnotebookの起動時に自動で実行されます.
ここでは,以下のコードを入力しています.
1 2 3 4 5 6 7 8 9 |
import numpy as np import pandas as pd import matplotlib.pyplot as plt %matplotlib inline from tqdm import tqdm_notebook as tqdm from time import sleep from collections import Counter |
※ 00-import_list.py
の拡張子以外は命名規則を充たす任意の名前が使用可能です.
※ 拡張子は py
でも良いですが,%matplotlib inline
などのマジックコマンドを書く場合は,拡張子を ipy
にしなければいけません.
また,他の方法でもnotebookの起動時にライブラリを読み込むことができます.
例えば, .ipython/profile_default/ipython_config.py
の c.InteractiveShellApp.exec_lines
に追記する方法です.
ここでは,コードの再利用という前提があるので,コードが複数行あることが想定されます.
その場合,c.InteractiveShellApp.exec_lines
に追記する方法よりもファイルごとに管理可能な startup
ファイルを作成する方法の方がより用途に合っているでしょう.
1.3 まとめ
notebookにおけるコードの再利用という問題に対して,ここでは2つの方法を紹介しました.
1つ目は,Jupyter notebook snippets menu
を使う方法です.
2つ目は,ipythonのstartup機能
を使う方法です.
ipythonのstartup機能
は,notebookの起動時に設定ファイルの全てのコードを暗黙に実行してしまうので,毎回,使用確実なライブラリのみを書くべきでしょう.
Jupyter notebook snippets menu
は,コード以外のテキストも登録することができるので,公開ライセンスの文言を登録しておくと便利かもしれません.
2 notebookのカラースキーム
読者の方々は普段どんなエディタを使っているでしょうか.
Visual Studio Codeでしょうか.それとも,Atom?,Vim?
チーム内でも様々なエディタが使われています.
では,カラースキームはどうでしょうか.
私はmonokaiを愛用していますが,社内ではsolarizedやICEBERGを使ったり,また自作したり,様々なカラースキームが使われています.
Jupyter notebookの場合はどうでしょう.
エディタのカラースキームを自分用に最適化している方は非常に多いです.
しかし,そのような方々でもJupyter notebookのカラースキームを変更している様子は滅多に見ません.
ここでは,Jupyter notebookのカスタムCSSを定義することでnotebookの見た目を自分好みに最適化する方法を紹介します.
notebookのカラースキームを変更する方法は主に3つあります.
本章では,notebookのカラースキームを変更する方法を示した後,notebookのCSSをどう変更すればいいのか,また,変更用のテンプレートを記載します.
2.1 custom.css
notebookのカラースキームを変更する方法の1つ目は,custom.cssを定義する方法です.
Jupyterは,HOMEディレクトリ以下の .jupyter
に設定用フォルダがあります.
.jupyter
フォルダ以下の custom
に custom.css
を配置することでnotebook起動時に適用することができます.
1 2 3 |
$ mkdir ~/.jupyter/custom $ touch ~/.jupyter/custom/custom.css |
しかしこの方法は,notebookの設定を変更してしまうため,個人に最適化しすぎた場合,他人にとって見づらくなり,また,再設定が手間になります.
2.2 IPython.display
notebookのカラースキームを変更する2つ目の方法は,notebookから IPython.display
を用いる方法です.
この方法は,上記の問題を解決してくれます.
IPython.display
は,IPythonのdisplayツール用のAPIで,音楽ファイルを再生するための IPython.display.Audio
やrawデータを画像に変換するための IPython.display.Image
などがあります[10].
その中でも, IPython.display.HTML
を用いて, style
要素をレンダリングすることでnotebookのCSSをオーバーライドすることができます.
notebookから IPython.display
を用いたCSSのオーバーライドは以下のようなコードで行うことができます.
1 2 3 4 |
from IPython.display import display, HTML css = !wget https://raw.githubusercontent.com/lapis-zero09/jupyter_notebook_tips/master/css/jupyter_notebook/monokai.css -q -O - css = "\n".join(css) display(HTML('<style type="text/css">%s</style>'%css)) |
上記のコードで今すぐお手元のJupyter notebookにロードすることができます
前述のSnippets Menuなどを使用すれば,このコードを覚える必要もありません.
また,この方法でJupyter Labのカラースキームを変更することもできます.
下画像で読み込んでいるCSSは,こちらです.
また,Jupyter notebookの場合と同様に今すぐお手元のJupyter Labにロードすることができます(CSSのURLを変更する必要があります).
また,本稿では詳細は示しませんが,Jupyter Labの場合,「Top Panel > Setting > JupyterLab Theme」に表示されるテーマとして追加することでボタンを押すだけでカラースキームを変更することができます.
Jupyter Labに内包される既存のlight-theme[12]などを参考にしてビルドすることでテーマとして追加することができます.
IPython.display
を用いる方法は便利ですが,Google ColaboratoryにCSSを適用することはできません.
2.3 Stylus[11]
notebookのカラースキームを変更する3つ目の方法は,Stylusを使用する方法です.
これは,Google ColaboratoryのCSSを変更することも可能です.
Stylusは,webページのスタイル変更を行うことができるブラウザエクステンションです.
URL毎に指定したCSSを適用することができます.
2.4 notebookのCSS定義方法
Jupyter notebook,Jupyter Lab,Google ColaboratoryのそれぞれのHTMLのclass属性やid属性は,微妙に異なっています.
しかし,セルの入力部分にはどれもCodeMirrorが使われています.
カラースキーム全体ではなく,シンタックスハイライトのみを変更したい場合は,CodeMirrorの要素を変更する40行ほどのCSSで対応することができます.
上記の画像をみてください.
左側はJupyter notebookオリジナルのシンタックスハイライトです.右側はone light風のシンタックスハイライトです.
one light風のシンタックスハイライトは,以下のようなCSSで再現することが可能です.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
.cm-s-ipython span.cm-comment { color: #75715e; font-style: italic; } .cm-s-ipython span.cm-keyword { color: hsl(301, 63%, 40%); font-weight: normal; } .cm-s-ipython span.cm-variable { color: hsl(230, 8%, 24%); } .cm-s-ipython span.cm-variable-2 { color: hsl(230, 8%, 24%); } .cm-s-ipython span.cm-variable-3 { color: hsl(230, 8%, 24%); } .cm-s-ipython span.cm-operator { color: hsl(230, 8%, 24%); } .cm-s-ipython span.cm-def { color: hsl(221, 87%, 60%); font-weight: normal; } .cm-s-ipython span.cm-property { color: hsl(221, 87%, 60%); } .cm-s-ipython span.cm-builtin { color: hsl(198, 99%, 37%); } .cm-s-ipython span.cm-number { color: hsl(41, 99%, 30%); } .cm-s-ipython span.cm-string { color: hsl(119, 34%, 47%); } |
上記のCSSとnotebook上のコードの対応は以下です.
- cm-comment:コメント
- cm-keyword:
if
,else
などの制御キーワード - cm-variable:変数
- cm-operator:
+
,=
などのオペレータ - cm-def:defの後に来る定義関数名
- cm-property:オブジェクトのプロパティ・メソッド
- cm-builtin:ビルトイン変数・関数
- cm-number:数字(小数点含む)
- cm-string:クォートで囲まれた文字
colorを変更して,自分のnotebookに適用してみてください.
上記で説明した通り,適用方法は,3つあります.
ここでは最も手軽な 2.2 IPython.display
で適用してみましょう.
以下のコードをnotebookで実行するだけです.
1 2 3 4 5 6 |
from IPython.display import display, HTML css = ''' **ここに作成したCSSを書く** ''' display(HTML('<style type="text/css">%s</style>'%css)) |
Google Colaboratoryに適用する際には, .cm-s-ipython
を .cm-s-default
に変更する必要があるので注意してください.
また,Jupyter notebookで .cm-s-default
は,notebook内のmarkdownセルのクラス属性を表すので注意が必要です.
Jupyter notebook,Jupyter Lab,Google ColaboratoryのmonokaiのカラースキームをGithubに上げていますので,背景色などを変更したい場合はこちらを参考にしてください.
jupyterthemesのテーマを元に自分好みに編集してもいいかもしれません.
- Jupyter notebook:https://github.com/lapis-zero09/jupyter_notebook_tips/tree/master/css/jupyter_notebook
- Jupyter Lab:https://github.com/lapis-zero09/jupyter_notebook_tips/tree/master/css/jupyter_lab
- Google Colaboratory:https://github.com/lapis-zero09/jupyter_notebook_tips/tree/master/css/colaboratory
Jupyter notbook monokail風CSSの例では,1~14行目に背景色,フォントのカラーパレットを定義しています.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
:root { --background-color: #1e1e1e; --cell-background-color: rgb(7, 7, 7); --focus-background-color: rgba(180, 180, 180, 0.14); --focus-color: #6e6c6c; --font-color: #f8f8f0; --dark-font-color: #d4d4d4; --disabled-font-color: #4b4b4b; --divider: rgba(180, 180, 180, 0.3); --input-prompt-color: #75715e; --output-prompt-color: #bf0000; --gutter: #49483e; } |
このカラーパレットを変更することでJupyter全体の背景色,フォント色の変更が可能なので簡単に独自のカラースキームを作成することができるかと思います.
シンタックスハイライトに関しては,361行目からの .cm-s-ipython span.**
の部分を前述の対応を見ながら変更すると良いでしょう.
2.5まとめ
ここではJupyter notebookのカスタムCSSを定義することでnotebookの見た目を自分好みに最適化する方法を3つ紹介しました.
1つ目は,custom.cssを定義する方法です.
2つ目は,notebookから IPython.display
を用いる方法です.
3つ目は,Stylusを使う方法です.
1つ目のcustom.cssを定義する方法はnotebookの設定を変更してしまうため,個人に最適化しすぎた場合,他人にとって見づらくなり,また,再設定が手間になります.そのため,個人仕様に限られた場合で使うのが適しているでしょう.notebookをダウンロードして共有する環境の場合もこの方法が適しています.
2つ目のIPython.display
を用いる方法は,CSSをロードするためのコードを使い回す必要があります.しかし,1 notebookにおけるコードの再利用
で紹介した方法を使用すれば,コピー&ペーストのための時間を取ることはないでしょう.
3つ目のStylusを使う方法は,上記2つの方法が適用できないGoogle ColaboratoryにもCSSを適用することが可能です.また,他の人に画面を少しの間見せるなどの場面で簡単にオリジナルのCSSに戻せるのでStylusの使用が可能であれば,最も良い方法かもしれません.
ぜひ,自分に合ったカラースキームを作ってみてください.
3 notebookでのコードのデバッグ
Jupyter notebookで動くコードを組み立てていく際,どのように組み立てていくでしょうか?
実行したセルが出力したエラーを愚直に修正していくでしょうか?
notebookが小さくコードが少ない場合はそれでもいいかもしれません.
しかし,notebookが大きくなり,セルが依存する変数が増え,また,メソッドやクラスが大きくなった時,その方法では徐々に辛くなるでしょう.
本章では,Jupyter notebookで動くコードを組み立てていく際のコードデバッグに関する技術を紹介します.
3.1 Embedding IPython
1 2 3 4 5 6 7 8 9 10 11 12 |
def foo(): a = list([i*2**2+1 for i in range(10)]) b = list([i if i%2==0 else 0 for i in range(2, 53, 5)]) c = a + b for i in range(len(c)-1): idx = int(i/2) c[i] += i + a[-idx] + b[idx] c[-i] += i + a[idx] + b[-idx] d = len([i*2**2+1 for i in range(-80, 130, 10)]) return c * d |
上記のような foo
という関数があるとします.
6行目の for
文終了時点で 変数c
の中身を確認したい場合あなたならどうしますか?
コード量が増加するほど,1行目から追って c
の中身を推定するのは理に適っていません.
変数d
の定義の前に return
を書いて c
を出力させて確認しますか?
それも悪くないかもしれません.ですが,もっといい方法があります.
それは, IPython.embed
を使う方法です.
IPython.embed
を使うことで,任意の箇所からIPythonセッションを開始することができます.
画像の例では,foo
関数の9行目に embed()
を差し込むことで,変数d
の定義の前にIPythonセッションを開始し,c
の中身を確認しています.
IPython.embed
で開始いしたIPythonセッションは一般的なIPythonシェルと同じ動作をします.
IPythonセッションから離脱する場合は, exit
と入力します.
3.2 ipdb
Pythonでは,対話型ソースコードデバッガとしてpdbが提供されています[14] (IPythonではipdb).
%debug
マジックコマンドはpdbの事後解析デバックを有効に活用する手段です.
例外発生後に%debug
マジックコマンドを使うことで,ipdbを起動し,例外が発生したコードの周辺を探索することができます.
1 2 3 4 5 6 7 8 9 10 11 12 |
def foo(): a = list([i*2**2+1 for i in range(10)]) b = list([i if i % 2 == 0 else 0 for i in range(2, 53, 5)]) c = a + b for i in range(len(c)-1): idx = int(i/2) c[i] += i + a[-idx] + b[idx] c[-i] += i + a[idx] + b[-idx] raise Exception d = len([i*2**2+1 for i in range(-80, 130, 10)]) return c * d |
例えば,上記のような foo
という関数があるとします.
この関数では,10行目で例外が発生することは自明です.
例外発生後に%debug
マジックコマンドを使うことで,ipdbを起動している様子がわかるかと思います.
今回の場合は例外の理由が自明ですが,ipdbには変数の中身を表示する機能や,Pythonコードを実行する機能など最低限の機能が実装されているので例外の発生原因を追求することが可能です.
ipdb(pdb)の操作方法については有志によってcheetsheetが作られているので参照すると良いでしょう.
3.3 その他デバッグに関するTips
3.3.1 tbマジックコマンド
%tb
マジックコマンドは最後のトレースバックを表示するマジックコマンドです.
例外が発生したセルの出力を上書きしてしまった場合でも,セルを元に戻す事なく以前のトレースバックを表示することができます.
3.3.2 InteractiveShell.ast_node_interactivity
InteractiveShell.ast_node_interactivity
により対話的に出力するコードの設定を行うことができます.
本来は,以下のように最後に評価した式のみ対話的に出力されます.
しかし,InteractiveShell.ast_node_interactivity = "all"
と設定することで,全ての式を対話的に出力することができます.
~/.ipython/profile_default/ipython_config.py
に設定を書き込むことで,全てのnotebookにこの設定を適用することも可能です.
1 2 3 4 5 6 |
!ipython profile create %%writefile -a ~/.ipython/profile_default/ipython_config.py c.InteractiveShell.ast_node_interactivity = "all" |
4 notebookを用いたプロファイリング
4.1 cProfileの活用
ここではJupyter notebookを用いたプロファイリングの方法について言及します.
実際,Jupyter notebookでは簡単にプロファイリングができます.
%timeit
ラインマジックコマンド,あるいは %%timeit
セルマジックコマンドによって timeit
モジュールを用いたコード実行時間の計測が行えることは周知の事実ですが,これらのコマンドはコードのどの部分で実行時間がかかっているかまで示してくれません.
Pythonでは,cProfileというプロファイラーが提供されています[13].
Jupyter notebookでは %prun
マジックコマンドと %%prun
セルマジックコマンドを使って,cProfileを簡単に活用することができます.
prunコマンドの結果は,少し見づらいと感じる方もいるかもしれません.
Jupyter notebookでは,line_profilerやmemory_profilerのマジックコマンドも使うことができます.
4.2 行ベースのプロファイリング
line_profilerは行毎にどのコードの実行に時間がかかったか,memory_profilerはコードの実行にどれだけメモリを使ったかが一目でわかります.
これらを使うには,まず,line_profilerとmemory_profilerをインストールします.
以下のコマンドでnotebookからライブラリをインストールします.
1 2 |
!pip install line_profiler memory_profiler |
次に %load_ext
マジックコマンドによってline_profilerとmemory_profilerのIPython拡張モジュールをインポートします.
1 2 3 |
%load_ext line_profiler %load_ext memory_profiler |
これによって, %lprun
マジックコマンドと %mprun
マジックコマンドが使えるようになります.
line_profilerは行毎にどのコードの実行に時間がかかったか,memory_profilerはコードの実行にどれだけメモリを使ったかが一目でわかります.
4.3 IPython拡張のオートロード
しかし,このままでは,毎回%load_ext
マジックコマンドでextensionのロードを行う必要があります.
IPython拡張をnotebookの起動時にインポートするには,c.InteractiveShellApp.extensions
を追記する方法があります.
この機能を使うには 1.2.1 startup機能使い方
と同様にプロファイルを作成する必要があります.
notebook上から以下のコードを実行することで,プロファイルの作成と拡張モジュールの自動読み込み設定が完了します.
1 2 3 4 5 6 7 8 |
!ipython profile create %%writefile -a ~/.ipython/profile_default/ipython_config.py c.InteractiveShellApp.extensions = [ 'line_profiler', 'memory_profiler', ] |
これによって立ち上げたばかりのnotebookでも,%lprun
マジックコマンドと %mprun
マジックコマンドが使えるようになります.
おわりに
ここまでご覧いただきありがとうございました.
本稿では,業務の内外で得たJupyterのTipsをいくつか挙げました.
今後も社内外でJupyterの知見をより展開できればと考えています.
最後になりましたが,本稿が皆様の作業の生産性を向上することができるような知見を提供できていれば幸いです.
参照
- https://recruit-tech.co.jp/blog/category/data-analytics/
- https://pycon.jp/2017/ja/schedule/presentation/38/
- https://pycon.jp/2017/ja/
- https://conferences.oreilly.com/jupyter/jup-ny/schedule/2018-08-21
- http://jupyter.org/
- https://github.com/jupyter/jupyter/wiki/Jupyter-kernels
- Mary Beth Kery et al. The Story in the Notebook: Exploratory Data Science using a Literate Programming Tool. CHI ’18. ACM, New York Paper 174, p.11.
- Adam Rule et al. Exploration and Explanation in Computational Notebooks. In Proceedings of the 2018 CHI Conference on Human Factors in Computing Systems (CHI ’18). ACM, New York, Paper 32, p.12.
- https://github.com/ipython-contrib/jupyter_contrib_nbextensions
- https://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html
- https://add0n.com/stylus.html
- https://github.com/jupyterlab/jupyterlab/tree/master/packages/theme-light-extension
- https://docs.python.jp/3/library/profile.html
- https://docs.python.jp/3/library/pdb.html
- https://gist.github.com/jasongrout/753216b2d3320b0abec6143d36f5d640