3年くらいデータサイエンティストをしています。仕事柄いろいろな環境でPythonを使うのですが、Jupyter Notebookなどノートブック形式のものを触ることも多いです1。
そんななかで、なんとなく意識している「ノートブックの冪等性」について書いてみます。
冪等とは何か
読み方は「べきとう」で、漢字が難しいので「べき等」と表記されることも多い。英語だと「idempotent」という超キュートな単語である。
雑に説明すると、冪等とはある操作を何回行っても結果が同じであることをいう。数式で書くと、以下が成り立つとき $f$ は冪等である2。
$$f(f(x)) = f(x)$$
おそらく最初は数学用語として登場したもので、統計学界隈だと線形回帰の時に登場する射影行列が冪等行列である3。それがエンジニアにも使われるようになり、Web系やデータエンジニアリングの本とかでちらほら見かける。おそらく、ネットワークが不安定とかで処理が上手いこと終わらなくて再実行したとしても変な結果にならないように冪等性を意識しているとかそんな感じ。
冪等でないと何が起きるか
ノートブックで汚くデータの加工・集計しているときに困るのも、同じことではないかと思う。
例で見ていく。
エラーが起きるようなコードを適当に実行してみる(2行目がダメ。apply難しいよね)。
これを修正して再実行を行う。
今度はうまくいった。やったね!
…やったね???
実はこれだと問題が生じている。jupyter notebookはセルの途中でエラーがあったときも、その前までの処理は完遂されている。つまり1行目は2回実行されており、v1
は元の100倍になってしまっているのだ。
これはこのセルが冪等でないことに起因する。
参考:pythonコード(クリックして展開)
import pandas as pd
df = pd.DataFrame({'v1': [2,4,6], 'v2': [10, 6, 11]})
df['v1'] = df['v1'] * 10
df['v3'] = df.apply(lambda x: 1 if x.v2 > 0 else 0) # これだとエラーが発生する
# df['v3'] = df.apply(lambda x: 1 if x.v2 > 0 else 0, axis=1) # これに直せば実行できるが。。
対処法
1. 毎回頭から全部実行する
それは流石に待てねえよ、あんちゃん
(とは言え長すぎるノートブックは事故の元なので、これも大事)
2. 別名で保存
違う名前のオブジェクトを都度作る。あるいはデータフレームだと、別の列を作って対処する。
が、保守的がすぎるような印象はある。
先ほどの例だと…(クリックして展開)
こうすれば冪等なセルになる。
import pandas as pd
df = pd.DataFrame({'v1': [2,4,6], 'v2': [10, 6, 11]})
df['v1_times_10'] = df['v1'] * 10
df['v3'] = df.apply(lambda x: 1 if x.v2 > 0 else 0, axis=1)
3. 関数にする
関数にすると途中で止まってくれるので、エラーを吐いて再実行という罠は逃れられる4。
kaggleだと make_FE
みたいな関数にしているのをよく見る(あれはたぶんテストデータにも同じ処理をするためだけど)
4. 放置。でもこの危険性があることは認識しておく
正直これでもいいと思う。前後行き来して試行錯誤しながらコーディングできることがnotebookの良い点なので、ここにカロリーをかける必要はない
ただし、この危険性があることは注意しておく必要がある。
最後に
調べてたらいくつか言及はありましたが5、ほぼヒットしなかったのでこんな概念は一般的ではない(あるいは違う言葉を使ってる?)ような気がします。ただ、みんな無意識にやっていることだとは思うので、Python初心者でノートブックをよく使う方など意識してみてはいかがでしょうか。
-
分かりやすいところだとGoogle ColabやKaggle Notebookは使っている人も多そう。databricksや、各種クラウドにもSagemakerなど該当するサービスがある。Python以外だと、jupyterのjuはjuliaのjuだし、Rにも(Rmarkdownとは別に)R Notebookがあるし、マイナーどころだがStataにも3年前くらいにNotebook機能がついている。 ↩
-
このあたりは非常にざっくりとした理解で、正確な表現ではないです。 ↩
-
https://ja.wikipedia.org/wiki/%E5%86%AA%E7%AD%89%E8%A1%8C%E5%88%97#%E5%BF%9C%E7%94%A8 ↩
-
これも副作用がある関数にしちゃったりすると結局意味ないですが。。 ↩
-
https://smacke.net/papers/nbsafety-cidr-gong.pdf や、 https://www.bradford-delong.com/2018/02/jupyter-notebook-programming-dos-and-donts-a-running-list.html など ↩