Jupyterで広がるPythonの可能性


2018/09/17 @大田区産業プラザPiO
[email protected]

だれ?

  • なまえ: どりらん
  • 本名: driller
  • Twitter: @patraqushe

しごと

  • デリバティブのトレーダー
    たまにデイトレーダーと間違えられる
    JavaとJavaScriptくらいの違い -> つまり大してかわらない
  • 最近はPythonの仕事もしている
In [3]:
%%tsumiki

:Markdown:

### 共著

:Markdown::
            
#### PythonユーザのためのJupyter[実践]入門


* Jupyterのつかいかた
* pandasの基礎
* データの可視化
  **Matplotlibの豆腐で困ってる人は本書を読むように!**
* クラウド上のJupyter
* R, RubyでJupyter
* スライド, ipywidgetsなど  


:HTML::
<img src="./static/images/jupyterbook.jpg" width=300 />
Out[3]:

共著


PythonユーザのためのJupyter[実践]入門

  • Jupyterのつかいかた
  • pandasの基礎
  • データの可視化
    Matplotlibの豆腐で困ってる人は本書を読むように!
  • クラウド上のJupyter
  • R, RubyでJupyter
  • スライド, ipywidgetsなど
In [4]:
%%tsumiki

:Markdown:

### Software Design 2018年2月号に寄稿
        
:Markdown::

第1特集「そのPythonライブラリ,どうして必要なんですか?」  
  
* 第1章:パッケージ管理の基礎を知ろう  
  効率的な開発のための前準備……石本 敦夫  
* 第2章:Pythonの基礎力を高めよう  
  ライブラリの使い方と作り方……くーむ
* 第3章:データ分析の前処理をさくっと終わらせよう  
  定時に帰るためのpandas入門……@driller  
* 第4章:イメージどおりにデータを可視化しよう  
  データに隠された意味を見つけるMatplotlib入門……片柳 薫子  

:HTML::
<img src="./static/images/sd201802.png" width=300 >
Out[4]:

Software Design 2018年2月号に寄稿

第1特集「そのPythonライブラリ,どうして必要なんですか?」

  • 第1章:パッケージ管理の基礎を知ろう
    効率的な開発のための前準備……石本 敦夫
  • 第2章:Pythonの基礎力を高めよう
    ライブラリの使い方と作り方……くーむ
  • 第3章:データ分析の前処理をさくっと終わらせよう
    定時に帰るためのpandas入門……@driller
  • 第4章:イメージどおりにデータを可視化しよう
    データに隠された意味を見つけるMatplotlib入門……片柳 薫子
In [5]:
%%tsumiki

:Markdown:
### fin-py

:Markdown::
* Python x 金融のコミュニティ
* PyConJP 2016をきっかけに発足
* もくもく会(月例)
* トークイベント(不定期)
* スピンオフイベント(オプション勉強会など)

:HTML::
<img src="./static/images/finpy_600x600.png" width=300 />
Out[5]:

fin-py

  • Python x 金融のコミュニティ
  • PyConJP 2016をきっかけに発足
  • もくもく会(月例)
  • トークイベント(不定期)
  • スピンオフイベント(オプション勉強会など)

ポスターセッションやってます

  • Fintech x IoTの事例
    社内でマイクロペイメント
  • 仮想通貨損益計算サービス
    Django
  • Pythonでファイナンシャルプランニング
    年金の最適化
  • Quantopian
    Pythonで株価分析・バックテスト
  • 衛星写真で原油生産状況を追う
In [20]:
%%tsumiki

:Markdown:
#### Agenda

:Markdown::
* Project Jupyter  
  Jupyterの歴史
* Jupyter Notebookの基本  
  基本を押さえて効率的に
* マジックコマンド  
  便利なコマンド集
* Unofficial Jupyter Notebook Extensions(nbextensions)  
  拡張機能で更に便利に

:Markdown::
* IPython.display  
  出力をカスタマイズ
* ipywidgets  
  インタラクティブに
* Jupyter Notebookでプレゼンテーション  
  資料作成を効率的に
* Jupyter Publishing  
  技術的な文書・WebサイトもJupyterで
* 便利なライブラリ・Webアプリケーション  
  更にJupyterを使いこなす
Out[20]:

Agenda

  • Project Jupyter
    Jupyterの歴史
  • Jupyter Notebookの基本
    基本を押さえて効率的に
  • マジックコマンド
    便利なコマンド集
  • Unofficial Jupyter Notebook Extensions(nbextensions)
    拡張機能で更に便利に
  • IPython.display
    出力をカスタマイズ
  • ipywidgets
    インタラクティブに
  • Jupyter Notebookでプレゼンテーション
    資料作成を効率的に
  • Jupyter Publishing
    技術的な文書・WebサイトもJupyterで
  • 便利なライブラリ・Webアプリケーション
    更にJupyterを使いこなす

1. Project Jupyter

IPython

  • 2001年 - Fernando Perez氏による開発
    当時からMaple, Mathematicaを使用、後のNotebookに影響を与える
  • REPLの拡張版
    • 型推定の強化
    • コード補完
    • マジックコマンド

IPython Notebook

  • 2011 - Fernando Pérez, Brian Granger氏による開発
  • データサイエンティストに対して生のコードを説明してもわかりにくかった
  • コードの追跡や共有を容易に
In [6]:
%%tsumiki

:Markdown::
### Project Jupyter
* 2014 - Fernando Pérez氏がIPythonからスピンオフしたJupyterプロジェクトを発表
* 名称は **Ju**lia **Py**thon **R** から
* 2018年9月時点では数十のコンピュータ言語に対応

:HTML::
<img src="./static/images/jupyter-logo.svg" width=300/>
Out[6]:

Project Jupyter

  • 2014 - Fernando Pérez氏がIPythonからスピンオフしたJupyterプロジェクトを発表
  • 名称は Julia Python R から
  • 2018年9月時点では数十のコンピュータ言語に対応

Project Jupyter

User Interfaces IPython Kernels Widgets
JupyterLab IPython IPython IPyWidgets/Jupyter Widgets
Jupyter Notebook IPyParallel IRkernel widget-cookiecutter
Jupyter Console Traitlets IJulia -
Qt Console - Community maintained kernels -
Notebook Documents JupyterHub Deployment Foundations
nbconvert JupyterHub Docker Stacks Jupyter Client
nbformat Zero to JupyterHub Kernel Gateway Jupyter Core
nbviewer nbgrader - Jupyter Alabaster Theme
- All JupyterHub Projects... - -

2. Jupyter Notebookの基本

Jupyter NotebookのUI

  • ノートブック
  • ファイルマネージャ
  • ターミナル

ファイルマネージャ

最低限のことはJupyter上で行える

  • 新規作成
  • 削除
  • リネーム
  • asciiファイルの編集

ターミナル

  • Jupyterを起動した状態の環境変数を引き継ぐ
  • 仮想環境が引き継がれるため、ライブラリのインストールが容易

エディタ

  • キーバインドが選べる
    • Sublime Text
    • Vim
    • emacs
  • シンタックスハイライト

Notebook

  • セルにコードを記述して即座に実行できる
    ターミナル上のREPLと比較して再編集が容易
  • マークアップ記法によるテキストの埋め込み
  • 画像出力(主にグラフ)の埋め込み
  • HTMLを埋め込み

3行でわかるJupyter Notebookのすごさ


vmstatコマンドの事例

1行目: vmstatコマンドを実行

In [9]:
%sc -l out=vmstat 1 5
out.l
Out[9]:
['procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----',
 ' r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st',
 ' 2  0      0 1900776 334136 1503092    0    0    72    25  212  613 13  2 84  1  0',
 ' 0  0      0 1900144 334136 1504660    0    0     0     0  896 2447 13  2 85  0  0',
 ' 1  0      0 1896548 334136 1507764    0    0     0     0 1416 3854 23  4 74  0  0',
 ' 1  0      0 1898168 334136 1504520    0    0     0     0 1174 3636 10  4 86  0  0',
 ' 1  0      0 1898036 334136 1504580    0    0     0     0  488 1523  3  1 96  0  0']

2行目: pandas.DataFrameに変換

In [10]:
df = pd.DataFrame(out.fields()[2:], columns=out.fields()[1], dtype=float)
df
Out[10]:
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 2.0 0.0 0.0 1900776.0 334136.0 1503092.0 0.0 0.0 72.0 25.0 212.0 613.0 13.0 2.0 84.0 1.0 0.0
1 0.0 0.0 0.0 1900144.0 334136.0 1504660.0 0.0 0.0 0.0 0.0 896.0 2447.0 13.0 2.0 85.0 0.0 0.0
2 1.0 0.0 0.0 1896548.0 334136.0 1507764.0 0.0 0.0 0.0 0.0 1416.0 3854.0 23.0 4.0 74.0 0.0 0.0
3 1.0 0.0 0.0 1898168.0 334136.0 1504520.0 0.0 0.0 0.0 0.0 1174.0 3636.0 10.0 4.0 86.0 0.0 0.0
4 1.0 0.0 0.0 1898036.0 334136.0 1504580.0 0.0 0.0 0.0 0.0 488.0 1523.0 3.0 1.0 96.0 0.0 0.0

3行目: 折れ線グラフに可視化

In [11]:
df.loc[:, "si":].plot(figsize=(12, 6));

1つのセルにまとめた場合

In [12]:
%sc -l out=vmstat 1 5
df = pd.DataFrame(out.fields()[2:], columns=out.fields()[1], dtype=float)
df.loc[:, "si":].plot(figsize=(12, 6));

3行できたこと

  • OSのコマンド結果をPythonのオブジェクトとして扱う
  • テーブルデータをHTMLでみやすく表示
  • データを可視化して埋め込み

Jupyter Notebookの便利帳

Shift + Tab

shift+tab

obj?

Docstringを確認できる obj?

obj??

ソースコードを確認できる obj?

?obj.*

オブジェクトをワイルドカードで検索できる ?obj*

In [30]:
%%tsumiki

:Markdown::
#### `_`, `__`, `___` (アンダースコア)
`_`: 直前の **実行結果**
`__`: 2つ前の **実行結果**
`___`: 3つ前の **実行結果**

<p>
実行した順番 ≠ セルの順番
</p>

:Markdown::
![obj?](./static/images/_,__,___.png)
Out[30]:

_, __, ___ (アンダースコア)

_: 直前の 実行結果
__: 2つ前の 実行結果
___: 3つ前の 実行結果


実行した順番 ≠ セルの順番

obj?

In [15]:
%%tsumiki

:Markdown::
#### `_番号`
`_1`: セル番号1の実行結果
`_2`: セル番号2の実行結果
`_3`: セル番号3の実行結果

:Markdown::
![obj?](./static/images/_n.png)
Out[15]:

_番号

_1: セル番号1の実行結果
_2: セル番号2の実行結果
_3: セル番号3の実行結果

obj?

In [16]:
%%tsumiki

:Markdown::
#### `_i`, `_ii`, `_iii`
`_i`: 直前に **実行した** 入力
`_ii`: 2つ前に **実行した** 入力
`_iii`: 3つ前に **実行した** 入力

<p>
実行した順番 ≠ セルの順番
</p>

:Markdown::
![obj?](./static/images/_i,_ii,_iii.png)
Out[16]:

_i, _ii, _iii

_i: 直前に 実行した 入力
_ii: 2つ前に 実行した 入力
_iii: 3つ前に 実行した 入力


実行した順番 ≠ セルの順番

obj?

In [17]:
%%tsumiki

:Markdown::
#### `_i1`, `_i2`, `_i3`
`_i1`: セル番号1の入力
`_i2`: セル番号2の入力
`_i3`: セル番号3の入力


:Markdown::
![obj?](./static/images/_i1,_i2,_i3.png)
Out[17]:

_i1, _i2, _i3

_i1: セル番号1の入力
_i2: セル番号2の入力
_i3: セル番号3の入力

obj?

In [18]:
%%tsumiki

:Markdown::
#### `_dh`, `_oh`
`_dh`: Notebookのカレントディレクトリ
`_oh`: セル番号と実行結果の辞書
`_ih`: **入力した順番** リスト

:Markdown::
![obj?](./static/images/_dh,_oh_ih.png)
Out[18]:

_dh, _oh

_dh: Notebookのカレントディレクトリ
_oh: セル番号と実行結果の辞書
_ih: 入力した順番 リスト

obj?

標準出力は拾わない

In [23]:
print(1)
1
In [24]:
_
Out[24]:
''

事例:データの前処理を試す

  • ,を取り除く
  • 型変換する
  • 線形補間する
In [25]:
data = pd.Series(['1000', '2,000', np.nan, '4,000'])
data
Out[25]:
0     1000
1    2,000
2      NaN
3    4,000
dtype: object

通常の処理1

メソッドをつないでいく
data.method().method().method()...

  • 途中経過がわからない
  • 間の処理の問題を気づきにくい

通常の処理2

別オブジェクトに格納していく

do_something1 = data.method()
do_something2 = do_something1.method()
do_something3 = do_something2.method()

  • オブジェクト名を決めるのが大変
    • 名前のバッティング
    • 意味のある名前をつけようとすると大抵長くなる

_の活用例

,を取り除く

In [26]:
_.str.replace(",", "")
Out[26]:
0    1000
1    2000
2     NaN
3    4000
dtype: object

_の活用例

型変換する

In [27]:
_.astype(float)
Out[27]:
0    1000.0
1    2000.0
2       NaN
3    4000.0
dtype: float64

_の活用例

線形補間する

In [28]:
_.interpolate()
Out[28]:
0    1000.0
1    2000.0
2    3000.0
3    4000.0
dtype: float64

まとめ

  • 最終的には下記のようにつなげればよいことがわかる
  • 途中経過を確認できる
In [29]:
"".join((_iii, _ii, _i)).replace("_", "")
# このような書き方も
# "".join(_ih[start:end]).replace("_", "")
Out[29]:
'.str.replace(",", "").astype(float).interpolate()'

!, !!

  • !を行頭につけるとOSのコマンドが実行できる
  • !!上記の機能に加え、結果をリストに格納できる

!の例

In [31]:
!ls sample
file1.csv  file2.csv
In [32]:
!cat sample/file1.csv
111,222,333
100,200,300

!!の例

In [33]:
!!cat sample/file1.csv
Out[33]:
['111,222,333', '100,200,300']

3. マジックコマンド

マジックコマンドとは

  • IPython独自のコマンド
  • line magic: 1行で実行するコマンド
  • cell magic: セル全体に適用されるコマンド
  • ユーザがマジックコマンドを作成できる(拡張マジック)

line magic

マジックコマンドのリファレンス

  • %magic
    すべてのマジックコマンドのリファレンス
  • %lsmagic
    マジックコマンドのリスト
  • %quickref
    IPythonのクイックリファレンス
In [34]:
%%tsumiki

:Markdown:
### OSコマンド関連

:Markdown::
#### ディレクトリ  
* %cd, %pwd, 
#### ファイル・ディレクトリ操作
* %cp, %mv, %rm, %rmdir
#### ls
* %ldir, %lf, %lk, %ll, %ls, %lx

:Markdown::
#### ファイルの内容
* %cat, %less, %more
#### man
* %man
#### 環境変数
* %env, %set_env
Out[34]:

OSコマンド関連

ディレクトリ

  • %cd, %pwd,

ファイル・ディレクトリ操作

  • %cp, %mv, %rm, %rmdir

ls

  • %ldir, %lf, %lk, %ll, %ls, %lx

ファイルの内容

  • %cat, %less, %more

man

  • %man

環境変数

  • %env, %set_env

OSコマンド関連

  • !で代用できるコマンドだがOS依存がなくなる
    Windowsでlspwdが気軽にできる!
  • 結果をオブジェクトに格納できるコマンドがある
    • %pwd -> str
    • %env -> dict

名前空間関連

  • %who, %who_ls, %whos
  • %reset, %reset_selective, %xdel
  • %psearch
  • %pinfo, %pinfo2

名前空間関連

  • Notebook上では名前空間が汚染されることが多い
    • 一度セルを実行すると、セルを消してもglobalsに残る
    • 意図せぬ結果となりバグの温床に
  • マジックコマンドでオブジェクトを管理
    • 一覧
    • 検索
    • 情報取得
    • 消去、リセット

%who, %who_ls, %whos

  • ユーザが作成したglobals()の内容を表示
    __name__などの特殊なオブジェクトは出てこない
In [40]:
i = 1
s = "a"
%whos
Variable   Type    Data/Info
----------------------------
i          int     1
s          str     a

デバッグ関連

  • %debug, %pdb

プロファイル関連

  • %prun

%prun

prun

実行関連

  • %run, %rerun, %rep, %recall
  • %sc, %sx, %system
  • %time, %timeit

%system, %sx, %sc

  • %system, %sx
    !!と等価
    コマンドの実行結果をリストに取り込める
  • %sc
    %sxよりも詳細な挙動を設定できる
    • -l:結果をリストに格納
    • -s:空白文字をセパレータとして処理

%time, %timeit

  • %time
    コードの実行時間を出力
  • %timeit
    コードを複数回実行して統計した結果を出力
    さまざまなオプションを設定できる

拡張関連

  • %load_ext
    拡張マジックを呼び出す
    マジックコマンドはPythonで自作できる
  • %unload_ext
    ロードしたマジックコマンドをアンロード

cell magic

%%writefile, %%file

  • セルに記述した内容をファイルに書き出す
  • モジュール化したい場合などに便利
  • 外部データやモジュールが必用な場合でも、1つのNotebookで完結できる -> 配布が楽
In [42]:
%%writefile sample/hello.py

def func():
    return "hello world"
Writing sample/hello.py
In [43]:
from sample import hello

hello.func()
Out[43]:
'hello world'

%%time, %%timeit

%time, %timeitをセル全体に対して実行できる

%%!, %%system, %%sx

!!, %system, %sxをセル全体に対して実行できる

In [44]:
%%sx

cat sample/file1.csv
echo
cat sample/file2.csv
Out[44]:
['111,222,333', '100,200,300', 'AAA,BBB,CCC']

%%capture

標準出力と標準エラーをオブジェクトに格納できる

In [45]:
%%capture capture_out

print(1)
In [46]:
capture_out.stdout
Out[46]:
'1\n'

%%bash

bashを実行する

In [47]:
%%bash

for file in sample/*.csv
do
  grep 1 $file
done
111,222,333
100,200,300

%%python2

python2のコードを実行する

In [48]:
%%python2

def func():
    yield 1

func().next()
print range(3)
[0, 1, 2]

%%pypy

  • PythonのコードをPyPyから実行する
  • pypy3ではなくpypyを呼び出す
    pypy3にする場合には環境変数やPATHを工夫する

Pythonで実行した場合

In [49]:
import time

start = time.time()
for x in range(1000000):
    x ** 2

print(time.time() - start)
0.490006685256958

PyPyで実行した場合

In [50]:
%%pypy

import time

start = time.time()
for x in range(1000000):
    x ** 2

print(time.time() - start)
0.007111549377441406

実行速度が70倍に!

%%javascript, %%js

JavaScriptのコードを実行

In [20]:
%%javascript

function fizzbuzz(n) {
    if (n % 3 == 0 && n % 5 == 0) return "Fizz,Buzz";
    else if (n % 3 == 0) return "Fizz";
    else if (n % 5 == 0) return "Buzz";
    else return n;
}

console.log(element.text(fizzbuzz(5)))

%%ruby

Rubyのコードを実行

In [52]:
%%ruby

def fizzbuzz(n)
    if n%15==0
        return "FizzBuzz"
    elsif n%3==0
        return "Fizz"
    elsif n%5==0 
        return "Buzz"
    else
        return n
    end
end

puts fizzbuzz(3)
Fizz

%%perl

Perlのコードを実行

In [53]:
%%perl

sub fizzbuzz {
    my ($n) = @_;
    if ( $n % 3 == 0 && $n % 5 == 0 ) {
        return "Fizz,Buzz";
    }
    elsif ( $n % 3 == 0 ) {
        print "Fizz";
    }
    elsif ( $n % 5 == 0 ) {
        return "Buzz";
    }
    else {
        return $n;
    }
}

print fizzbuzz(15)
Fizz,Buzz

%%script

  • 引数をシバン(#!)のように扱える
    つまり、なんでもできる

juliaの実行例

In [54]:
%%script julia

function fizzbuzz(n)
    if n % 15 == 0
        println("fizzbuzz")
    elseif n % 5 == 0
        println("buzz")
    elseif n % 3 == 0
        println("fizz")
    else
        println(n)
    end
end 

println(3)
fizzbuzz (generic function with 1 method)
3

display関連

セルに記述された内容をレンダリングして出力
詳細はIPython.displayで解説

  • %%HTML, %%html
  • %%SVG, %%svg
  • %%markdown
  • %%latex

Jupyter NotebookでSQL

ipython-sql

  • 拡張マジックコマンド
    • %sql, %%sql
  • SQLAlchemyからDBに接続
  • pandas.DataFrameに対応

テーブルを作成

In [55]:
%load_ext sql
In [56]:
%%sql sqlite://
CREATE TABLE sample_table (name, age);
INSERT INTO sample_table VALUES ('Nagato', 16);
INSERT INTO sample_table VALUES ('Mikuru', 17);
Done.
1 rows affected.
1 rows affected.
Out[56]:
[]

SELECTの結果をDataFrameに

In [57]:
%%sql
SELECT * from sample_table;
 * sqlite://
Done.
Out[57]:
name age
Nagato 16
Mikuru 17
In [58]:
_.DataFrame()["age"].mean()
Out[58]:
16.5

4. Unofficial Jupyter Notebook Extensions

Unofficial Jupyter Notebook Extensions

※ 以降はnbextensionsと表記

  • Notebookに機能を追加する拡張機能群
  • JavaScript
  • ローカルブラウザで動作

ExecuteTime

  • セルの実行時間が表示される
  • デフォルトで表示されるため、%%timeよりお手軽

ExecuteTime

Snippets

  • ドロップダウンで登録済みのスニペットをセルに貼付け
  • 任意のコードやマークアップなどを登録できる
  • さまざまな用途に
    • import
    • docstringのテンプレート
    • チートシート代わり

よくやるimport処理

import matplotlib

In [ ]:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

docstringのテンプレート

import matplotlib

In [ ]:
def func(arg1, arg2):
    """Summary line.
    
    Extended description of function.
    
    Parameters
    ----------
    arg1 : int
        Description of arg1
    arg2 : str
        Description of arg2
    
    Returns
    -------
    bool
        Description of return value
    
    """
    return

Snippets Menu

  • 予めよくつかわれるスニペットが登録されている
    • numpy, scipy, matplotlib, sympy, pandas
    • astropy, h5py, numba
    • python
    • markdown
  • カスタマイズできる

Jupyter Notebookでもきれいなコードを書く

pycodestyle_magic

  • 拡張マジック
  • pycodestyleのcell magic
  • PEP8に違反した箇所を出力
  • コードは実行しないので注意
In [61]:
%load_ext pycodestyle_magic
In [62]:
%%pycodestyle

def f(x,y ):
    return x**+y
print(3,2)
2:8: E231 missing whitespace after ','
2:10: E202 whitespace before ')'
4:1: E305 expected 2 blank lines after class or function definition, found 0
4:8: E231 missing whitespace after ','

Ruler

  • nbextensions
  • 指定した文字数に縦線を描画
  • コード1行当たりの文字数の上限を可視化できる
  • 線の色やスタイルが変更できる
  • 線は複数設定できる
    インデントの可視化など

Ruler

isort formatter

  • isortのnbextensions
  • ボタン1発でimport文を並び替える

Before

In [ ]:
import pandas as pd
import numpy as np
from datetime import datetime
import ast

After

In [ ]:
import ast
from datetime import datetime

import numpy as np
import pandas as pd

コードフォーマッタ

おだい:このコードをなんとかする

pd.DataFrame(data={"A":[1000,2000,3000,4000,5000,6000],"B":[.001,.002,.003,.004,.005,.006],},index=range(1,7),dtype=int,).reindex(range(1,10)).append(pd.DataFrame([[8000,.008]],columns=["A","B"])).interpolate(method="index",axis=0,limit=10000,inplace=False,limit_direction="forward",limit_area="inside",downcast="infer",)

Autopep8

ボタン1発で整形できる

In [ ]:
pd.DataFrame(data={"A": [1000, 2000, 3000, 4000, 5000, 6000], "B": [.001, .002, .003, .004, .005, .006], }, index=range(1, 7), dtype=int,).reindex(range(1, 10)).append(
    pd.DataFrame([[8000, .008]], columns=["A", "B"])).interpolate(method="index", axis=0, limit=10000, inplace=False, limit_direction="forward", limit_area="inside", downcast="infer",)

Code prettify(yapf)

ボタン1発で整形できる

In [ ]:
pd.DataFrame(
    data={
        "A": [1000, 2000, 3000, 4000, 5000, 6000],
        "B": [.001, .002, .003, .004, .005, .006],
    },
    index=range(1, 7),
    dtype=int,
).reindex(range(1, 10)).append(
    pd.DataFrame([[8000, .008]], columns=["A", "B"])).interpolate(
        method="index",
        axis=0,
        limit=10000,
        inplace=False,
        limit_direction="forward",
        limit_area="inside",
        downcast="infer",
    )

%%blackcellmagic

セルマジックを使う

In [ ]:
%load_ext blackcellmagic
In [ ]:
%%black
pd.DataFrame(data={"A":[1000,2000,3000,4000,5000,6000],"B":[.001,.002,.003,.004,.005,.006],},index=range(1,7),dtype=int,).reindex(range(1,10)).append(pd.DataFrame([[8000,.008]],columns=["A","B"])).interpolate(method="index",axis=0,limit=10000,inplace=False,limit_direction="forward",limit_area="inside",downcast="infer",)
In [ ]:
pd.DataFrame(
    data={
        "A": [1000, 2000, 3000, 4000, 5000, 6000],
        "B": [.001, .002, .003, .004, .005, .006],
    },
    index=range(1, 7),
    dtype=int,
).reindex(range(1, 10)).append(
    pd.DataFrame([[8000, .008]], columns=["A", "B"])
).interpolate(
    method="index",
    axis=0,
    limit=10000,
    inplace=False,
    limit_direction="forward",
    limit_area="inside",
    downcast="infer",
)

jupyter-black

  • blackはnbextensionsがない(2018/09/17時点)
  • ないので作った
  • ほかのフォーマッタ同様に Black ボタン1発でコードを整形できる
  • 公式でnbextensionsを出してくれたらお役御免に

5. IPython.display

IPython.display

  • DisplayObjectを継承したクラス
  • 文字列やURLから得たデータをレンダリングしてNotebook上に表示
  • cell magicで代用できるものが多い

HTML

  • raw HTMLデータを表示
  • URLやローカルPATHを指定できる
  • JavaScriptも動作する
  • これさえ使えれば、表示上の問題は大抵どうにかなる
In [63]:
display.HTML("<font color=red>Red</font>")
Out[63]:
Red

HTMLを利用してNotebookのスタイルも変更できる

実行前 width before 実行後 width after

横幅を有効活用

Markdown

  • マークダウン記法で記述したテキストを表示
  • URLやローカルPATHを指定できる
  • GFMに対応
In [7]:
%%markdown
col1|col2
----|----
data|data
col1 col2
data data

Markdownの応用事例

  • format関数やf-stringと組み合わせられる
  • Jinja2と組み合わせるといろいろできる

現在時刻に合わせてMarkdownであいさつする

In [8]:
hour = datetime.datetime.now().timetuple().tm_hour
oha = 4 <= hour < 11
kon = 11 <= hour < 18
ban = 18 <= hour or 0 <= hour < 4

greetings = np.array(["おはようございます", "こんにちは", "こんばんは"])
greeting = greetings[np.array([oha, kon, ban])][0]
display.Markdown(f"# みなさん{greeting}")
Out[8]:

みなさんこんにちは

The Zen of PythonをMarkdownで表示する

In [ ]:
zen_op_python = "".join([this.d.get(c, c) for c in this.s]).split("\n")

tpl_text = """
#### {{ title }}
{% for item in item_list %}
  * {{ item }}
{% endfor %}
"""

tpl = Template(tpl_text)
display.Markdown(
    tpl.render({"title": zen_op_python[0], "item_list": zen_op_python[2:]})
)
In [67]:
zen_op_python = "".join([this.d.get(c, c) for c in this.s]).split("\n")

tpl_text = """
#### {{ title }}
{% for item in item_list %}
  * {{ item }}
{% endfor %}
"""

tpl = Template(tpl_text)
display.Markdown(
    tpl.render({"title": zen_op_python[0], "item_list": zen_op_python[2:]})
)
Out[67]:

The Zen of Python, by Tim Peters

  • Beautiful is better than ugly.

  • Explicit is better than implicit.

  • Simple is better than complex.

  • Complex is better than complicated.

  • Flat is better than nested.

  • Sparse is better than dense.

  • Readability counts.

  • Special cases aren't special enough to break the rules.

  • Although practicality beats purity.

  • Errors should never pass silently.

  • Unless explicitly silenced.

  • In the face of ambiguity, refuse the temptation to guess.

  • There should be one-- and preferably only one --obvious way to do it.

  • Although that way may not be obvious at first unless you're Dutch.

  • Now is better than never.

  • Although never is often better than right now.

  • If the implementation is hard to explain, it's a bad idea.

  • If the implementation is easy to explain, it may be a good idea.

  • Namespaces are one honking great idea -- let's do more of those!

  • PATHのハイパーリンクを作成
  • GitHubのリポジトリから参照させたい場合などに便利
In [69]:
display.FileLinks("./sample/")
Out[69]:
./sample/
  file2.csv
  file1.csv

Image, SVG, Video, YouTubeVideo

  • 画像・動画を表示
  • URLやPATHの指定ができる

display

  • オブジェクトをフロントエンドとして表示
    pandas.DataFrameなど
  • 表示形式をカスタマイズできる

displayの使用例

In [71]:
df1 = pd.DataFrame([[1, 2], [3, 4]])
df2 = pd.DataFrame([[10, 20], [30, 40]])

df1
df2
Out[71]:
0 1
0 10 20
1 30 40

通常は最後のオブジェクトしか表示されない

In [72]:
display.display(df1)
display.display(df2)
0 1
0 1 2
1 3 4
0 1
0 10 20
1 30 40

カスタマイズ例

In [73]:
class Json(object):
    def __init__(self, json):
        self.json = json

    def _repr_pretty_(self, pp, cycle):
        import json

        pp.text(json.dumps(self.json, indent=2))

    def __repr__(self):
        return str(self.json)

display.display(Json({1: 2, 3: {4: 5}}))
{
  "1": 2,
  "3": {
    "4": 5
  }
}

clear_output

  • 表示を消去する
  • 表示した情報を更新する場合に便利
    • 出力を追記ではなく上書きする
    • 画像を表示して差し替える(アニメーション)
In [9]:
for i in range(5):
    print(i)
    time.sleep(2)
    display.clear_output(wait=True)
4

update_display

  • displayで一度表示した情報を更新
  • 複数の表示情報を制御できる
  • ipywidgetsと組み合わせると便利
In [10]:
display.display(pd.DataFrame(np.random.rand(4, 4)), display_id="df1")
0 1 2 3
0 0.538430 0.614457 0.181284 0.368867
1 0.092572 0.664070 0.329452 0.661846
2 0.956032 0.178922 0.199446 0.017380
3 0.248847 0.494473 0.240021 0.827550
Out[10]:
<DisplayHandle display_id=df1>
In [11]:
for i in range(5):
    display.update_display(pd.DataFrame(np.random.rand(4, 4)), display_id="df1")
    time.sleep(1)

6. ipywidgets

ipywidgets

  • Notebook上にインタラクティブなUIを提供
  • 多数のインタフェース
    Button, Slider, Dropdown, RadioButtons, Date picker...
  • 実装が容易

3行でできるUI

In [12]:
@interact(checked=True, selected=["A", "B", "C"])
def f(checked, selected):
    return checked, selected

事例:ファンドの組み入れ比率

  • 複数の投資信託の投資比率を考えてみる
    ファンドA, ファンドB, ファンドC
  • できるだけリターンを大きくしたい
  • できるだけリスクを小さくしたい

投資比率をパラメータ化

  1. リターンとリスクを算出する関数を作成
  2. パラメータを変更して1.の結果を確認
  • パラメータを手入力で変更するのは大変
  • すべての組み合わせを一覧化・可視化すると情報が多すぎる
  • 状況に応じて裁量でパラメータを調整したい

ipywidgetsでインタラクティブにリターンとリスクを可視化

In [13]:
def fund_returns(s0, mu, sigma, t, dt):
    return s0 * np.exp(
        np.cumsum(np.random.normal(mu * dt, sigma * np.sqrt(dt), round(t / dt)))
    )


def get_return_and_risk(a, b):
    weight = (a, b, 1 - sum((a, b)))
    return_df = df * weight
    return return_df.sum(axis=1).pct_change().var(), return_df.iloc[-1].sum() / 100


np.random.seed(9)
fund_a = fund_returns(100, 0.1, 0.15, 1, 0.01)
fund_b = fund_returns(100, 0.12, 0.4, 1, 0.01)
fund_c = fund_returns(100, 0.05, 0.1, 1, 0.01)
df = pd.DataFrame({"A": fund_a, "B": fund_b, "C": fund_c})


@interact(
    a=FloatSlider(0.3, min=0.0, max=1.0, step=0.1, continuous_update=False),
    b=FloatSlider(0.3, min=0.0, max=1.0, step=0.1, continuous_update=False),
)
def plot_return_and_risk(a, b):
    c = 1 - sum((a, b))
    fig = plt.figure(figsize=(14, 6))
    ax1 = fig.add_subplot(1, 2, 1)
    ax1.plot(fund_a, label="FundA")
    ax1.plot(fund_b, label="FundB")
    ax1.plot(fund_c, label="FundC")
    ax2 = fig.add_subplot(1, 2, 2)
    ax1.legend()
    ax2.set_title(f"a:{a:.2}0%, b:{b:.2}0%, c:{c:.2}0%", fontsize=20)
    ax2.set_xlim(0, 0.002)
    ax2.set_xlabel("Risk", fontsize=20)
    ax2.set_ylabel("Return", fontsize=20)
    ax2.set_ylim(0.8, 1.5)
    ax2.scatter(*get_return_and_risk(a, b), s=1000)
    ax2.axvline(0.001, c="black")
    ax2.axhline(1.0, c="black")

7. Jupyter Notebookでプレゼンテーション

資料作成の悩み

  1. 資料に使えそうなコードを書く
  2. コードや実行結果をスライドに転記する
  3. 1.を途中で変更する
  4. ウギャーとなる
    • 転記が大変
    • 漏れが出る

Jupyter Notebook

  • スライドに変換する機能がある(nbconvert)
  • マークダウンで書ける
  • コードも実行結果もそのままスライドにできる
  • ウギャーとならない

Jupyter Notebookでいいんじゃね?

  • 変換がメンドウ
  • 変換後はコードが実行できない

RISE

  • Reveal.jsを利用したnbextensions
  • ボタン1発でNotebookからスライドに
  • スタイルをカスタマイズできる
  • スライド上からコードを実行できる
  • nbpresentというツールもあるらしい

RISEを使うとインタラクティブなプレゼンが可能に

コードが邪魔

  • 大抵において、プレゼン資料で必用なのはコードではなく実行結果
    • テーブルデータ(pandas.DataFrameなど)
    • グラフ(Matplotlibなど)
  • 隠せるものなら隠したい
    • 全部のコード
    • 一部のコード

Hide_code

  • nbextensions
  • チェックした項目を隠せる
    • [ ] Hide Prompts
    • [ ] Hide Code
    • [ ] Hide Outputs

cellの内容を固定したい

  • コードは見せたいが実行したくない
  • 一度実行した結果を固定したい

Freeze

セルを読み取り専用にしたり、実行不可にできる

  • read-only
    変更したくないセルにプロテクトをかけられる
  • unlock
  • frozen(実行不可)
    Rull Allを実行した際に、実行したくないセルをスキップできる
In [75]:
%%tsumiki

:Markdown:
#### 段組みにしたい

:Markdown::
* よのなかのアスペクト比はどんどん横長に
* プレゼン資料で横長の文章は読みづらい
* 左右に分割して比較をしたい

:Markdown::
* ここには
* 有効なスペースが
* ありますよ
Out[75]:

段組みにしたい

  • よのなかのアスペクト比はどんどん横長に
  • プレゼン資料で横長の文章は読みづらい
  • 左右に分割して比較をしたい
  • ここには
  • 有効なスペースが
  • ありますよ

tsumiki

  • どりらんが考案したマークアップ記法
  • MarkdownやHTMLで記載した内容を複数列に表示できる
  • IPython/Jupyter Notebookのcell magicに対応
  • Jinja2に対応
In [31]:
%%tsumiki

:Markdown:
#### Main Title
:Markdown::
##### Sub Title1
* col1
* Markdown
:HTML::
<h5>Sub Title2</h5>
<ul>
  <li><font color="red">col2</font></li>
  <li><font color="green">HTML</font></li>
</ul>
Out[31]:

Main Title

Sub Title1
  • col1
  • Markdown
Sub Title2
  • col2
  • HTML

Jinja2に対応

In [14]:
today = datetime.date.today()
tenki = [["23区", "晴れ"], ["練馬区", "ゲリラ豪雨"]]
In [15]:
%%tsumiki -r

:Markdown:
### {{ today }}日の天気は
{% for loc, weather in tenki %}
  {{ loc }}は{{ weather }}
{% endfor %}
Out[15]:

2018-10-18日の天気は

23区は晴れ
練馬区はゲリラ豪雨

pandas.DataFrame

In [16]:
df1 = pd.DataFrame(np.random.rand(4, 4))
df2 = pd.DataFrame(np.random.rand(4, 4))
df1_html = df1.to_html()
df2_html = df2.to_html()
In [17]:
%%tsumiki -r

:HTML::
{{ df1_html }}

:HTML::
{{ df2_html }}
Out[17]:
0 1 2 3
0 0.619147 0.189785 0.935151 0.708119
1 0.801716 0.780755 0.523305 0.631772
2 0.018740 0.583013 0.609735 0.667796
3 0.203620 0.320793 0.973482 0.273809
0 1 2 3
0 0.699981 0.265305 0.572697 0.872142
1 0.817083 0.029230 0.424361 0.393610
2 0.900326 0.035806 0.454794 0.981336
3 0.036405 0.670754 0.734716 0.104887

Matplotlib

In [18]:
svg_data = StringIO()
fig, ax = plt.subplots()
ax.plot(df1)
fig.savefig(svg_data, format="SVG")
plt.close(fig)
svg_data.seek(0)
plot_data = svg_data.read()
In [19]:
%%tsumiki -r

:Markdown:
##### DataFrame and Plot

:HTML::
{{ df1_html }}
:HTML::
{{ plot_data }}
Out[19]:
DataFrame and Plot
0 1 2 3
0 0.619147 0.189785 0.935151 0.708119
1 0.801716 0.780755 0.523305 0.631772
2 0.018740 0.583013 0.609735 0.667796
3 0.203620 0.320793 0.973482 0.273809

tsumikiの今後

  • 対応マークアップ言語の拡充
    • reStructuredText
    • AsciiDoc
  • コマンドの実装
  • テーマ

8. Jupyter Publishing

技術的な情報を

  • Webサイトにまとめたい
  • ブログの記事として投稿したい
  • ドキュメントを書きたい
  • 技術書を書きたい

技術的な情報をまとめるのに大変なこと

  • コードとその解説のフォーマットが異なる
  • コードを記事や原稿に転記するのが大変
  • コードと情報のファイルが別々で管理が煩雑
  • 原稿に書いてあるコードのテストが難しい

Jupyter Notebookなら全部できる

  • 解説部分を軽量マークアップ言語で記述できて
    • 表や図、画像などの埋め込み
    • 数式を表現
    • raw HTML
  • 記述したコードを実行できて
  • 実行したコードの出力を保存できる

必用なもの

  • Jupyter Notebook
  • サイトジェネレータ or ドキュメントビルダー

That's All!

静的サイトジェネレータ・ドキュメントツール

  • Shpinx
    nbsphinxプラグイン
  • Pelican
    pelican-ipynbプラグイン
  • Nikola
    設定のみ
  • Miyadaiku
    設定のみ

実際にJupyter Notebookでかいたもの

9. 便利なライブラリ・Webアプリケーション

よくいわれること

「Jupyterってdiffがなぁ・・・」

$ diff plot1.ipynb plot2.ipynb 
22c22
<        "[<matplotlib.lines.Line2D at 0x7f1ecc596710>]"
---
>        "[<matplotlib.lines.Line2D at 0x7fb2184d9748>]"
31c31
<       "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAD8CAYAAACMwORRAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3XlcVXX+x/HXVwUVRFzAFRE33EBLUbOasmxKzRazmWraraym2X+TmtZo2WI2M00zLY41LU5NzSS4pGWrZYtWWsmmKOKGGygKCLLe7+8P+P0eTqlc9cDhnvt+Ph48As6J8/564e3x3MPnGmstIiLiLU3cDiAiIs5TuYuIeJDKXUTEg1TuIiIepHIXEfEglbuIiAep3EVEPEjlLiLiQSp3EREPaubWgaOiomxcXJxbhxcRCUjr1q3bb6 ...

nbdime

  • Notebookのdiffとmergeをやりやすくしたライブラリ
  • gitコマンドに対応
  • nbextensionを使うとボタン一発で履歴管理
In [27]:
%%tsumiki

:Markdown:
#### nbdiff

:Markdown::
```bash
$ nbdiff plot1.ipynb plot2.ipynb
--- plot1.ipynb  2018-09-09 18:10:56.678277
+++ plot2.ipynb  2018-09-09 18:11:11.428336
## modified /cells/1/outputs/0/data/text/plain:
-  [<matplotlib.lines.Line2D at 0x7f1ecc596710>]
+  [<matplotlib.lines.Line2D at 0x7fb2184d9748>]

## inserted before /cells/1/outputs/1:
+  output:
+    output_type: display_data
+    data:
+      image/png: iVBORw0K...<snip base64, md5=6d291fb88d778210...>
+      text/plain: <Figure size 432x288 with 1 Axes>


```
↗
:Markdown::
↘
```bash
## deleted /cells/1/outputs/1:
-  output:
-    output_type: display_data
-    data:
-      image/png: iVBORw0K...<snip base64, md5=f287ce2ad0448575...>
-      text/plain: <Figure size 432x288 with 1 Axes>

## modified /cells/1/source:
@@ -1,2 +1,2 @@
 fig, ax = plt.subplots()
-ax.plot([1, 2])

+ax.plot([1, 2], color="red")
```
Out[27]:

nbdiff

$ nbdiff plot1.ipynb plot2.ipynb
--- plot1.ipynb  2018-09-09 18:10:56.678277
+++ plot2.ipynb  2018-09-09 18:11:11.428336
## modified /cells/1/outputs/0/data/text/plain:
-  [<matplotlib.lines.Line2D at 0x7f1ecc596710>]
+  [<matplotlib.lines.Line2D at 0x7fb2184d9748>]
## inserted before /cells/1/outputs/1:
+  output:
+    output_type: display_data
+    data:
+      image/png: iVBORw0K...<snip base64, md5=6d291fb88d778210...>
+      text/plain: <Figure size 432x288 with 1 Axes>

## deleted /cells/1/outputs/1:
-  output:
-    output_type: display_data
-    data:
-      image/png: iVBORw0K...<snip base64, md5=f287ce2ad0448575...>
-      text/plain: <Figure size 432x288 with 1 Axes>
## modified /cells/1/source:
@@ -1,2 +1,2 @@
 fig, ax = plt.subplots()
-ax.plot([1, 2])
+ax.plot([1, 2], color="red")

nbdiff-web

nbdiff-web ボタンでも表示できる

papermill

  • Notebookを実行できる
    • Pythonから
    • コマンドから
  • 実行時にパラメータを渡せる
  • Notebookが実行した結果を記録して収集できる
  • ほかのNotebookで可視化した画像を表示できる
  • Netflix社が使ってるらしい

papermillの活用例

  • テスト問題をNotebookで作成
  • 解答したNotebookを実行・記録
  • 記録された解答を集計

ホスティング

GitホスティングサービスでNotebookがそのまま表示できる

  • GitHub
  • GitLab
  • BitBucket

nbviewer

http://nbviewer.jupyter.org

  • NotebookをレンダリングするWebアプリケーション
  • ブラウザがあればNotebookの表示ができる
  • JavaScriptが使われたコードも表示できる
  • スライド表示に対応
  • binderへのリンク
  • 独自のnbviewerサーバーをホストできる
In [81]:
%%tsumiki

:Markdown:
#### GitHub vs nbviewer

:HTML::
<h5>GitHub</h5>
<img src="static/images/render_github.png">

:HTML::
<h5>nbviewer</h5>
<img src="static/images/render_nbviewer.png">
Out[81]:

GitHub vs nbviewer

GitHub
nbviewer

binder

https://mybinder.org

  • Notebookの実行環境を提供するサービス
  • GitHubのリポジトリを指定するとDockerのコンテナで環境構築
    requirements.txt, environment.ymlなどのファイルでインストール環境を指定
  • JupyterHubが起動され、利用者はURLにアクセスするだけでNotebookを利用できる

Colaboratory

https://colab.research.google.com

  • インストール不要でNotebookが作成・実行できる
  • TensorFlowなどのライブラリがデフォルトで使える
  • GPUが使える
  • NotebookをGoogle Driveに保存できる
  • URLを渡すだけでGitHubリポジトリのNotebookを利用できる
  • 独自のフォーム機能
  • 共有したユーザは同時に読み書きできる

Colaboratory留意事項

  • セッションが切れると環境がリセットされる
    Notebookに書いた内容は残る
  • Notebookからローカルファイルを扱いたい場合
    • 都度、外部のストレージにupload/downloadする
    • Google Driveと連携させる
GPUの活用例

GPU+CuPyでNumpyと同じ処理を実行 numpy vs cupy 70倍の高速化

In [20]:
%%tsumiki

:Markdown:
#### 今回紹介したライブラリ

:Markdown::
* [Project Jupyter](http://jupyter.org)
* [Unofficial Jupyter Notebook Extensions](https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/index.html)
* [pycodestyle](http://pycodestyle.pycqa.org/en/latest/)
* [pycodestyle_magic](https://github.com/mattijn/pycodestyle_magic)
* [isort](http://timothycrosley.github.io/isort/)
* [autopep8](https://github.com/hhatto/autopep8)
* [yapf](https://github.com/google/yapf)
* [Black](https://black.readthedocs.io/en/stable/index.html)
* [blackcellmagic](https://github.com/csurfer/blackcellmagic)

:Markdown::
* [ipython-sql](https://github.com/catherinedevlin/ipython-sql)
* [Hide_code](https://github.com/kirbs-/hide_code)
* [RISE](https://damianavila.github.io/RISE/)
* [nbdime](https://nbdime.readthedocs.io/en/stable/)
* [papermill](https://papermill.readthedocs.io/en/latest/)
* [CuPy](https://cupy.chainer.org)
Out[20]:

今回紹介したライブラリ

今回紹介したWebアプリケーション

どりらんがつくったもの

まとめ

  • Jupyterを使いこなす
    • マジックコマンド
    • nbextensions
    • IPython.display
    • ライブラリ・Webアプリケーション
  • 色々な用途に
    • プレゼン資料
    • ドキュメント
    • ウェブサイト

ご清聴ありがとうございました

ポスターセッションもよろしくネ