AREKORE

daikikatsuragawaのアレコレ

ノートブックをキレイにするためのTips

「PyCon APAC 2023」のPoster Sessionにおいて発表した「ノートブックをキレイにするためのTips」を記事として書き起こしたものになります。

2023-apac.pycon.jp

ポスターは以下から閲覧が可能です。

speakerdeck.com

概要

Jupyter NotebookやGoogle Colaboratoryなどのノートブックは、データ分析などの目的で使用されるインタラクティブにコードの記述や実行が可能な開発環境です。作成したノードブックを他者や未来の自分が改めて閲覧や実行をする可能性がある場合、実装の意図を伝えるために可読性を高く、意図した振る舞いを担保するために堅牢性を高くすることが大事です。ただし、ノートブックはそれ自体がソフトウェアとして実装されるものではないことから、可読性や堅牢性を向上させるメリットとコストのトレードオフを特に注意する必要があります。本発表では、ノートブックの可読性や堅牢性を向上させる(キレイにする)ために、可能な限りコストをかけずにメリットを得る方法(Tips)を紹介します。ご自身の管理されているノートブックの状況に合わせて、採用を検討してください。

管理コストの低いパラメータの扱い方

調整の可能性があるパラメータは、様々なセルに記述するのではなく、可能な限り少ないセルに集約すると、見通しが良く、管理コストが低くなります。そして、ノートブックの冒頭にあると、さらに見通しが良いです。実行毎に指定する値は、実行者が入力する仕組みにすると、その旨を伝えられます。

パラメータの集約

# 乱数の種
RANDOM_SEED = 2023
# PyCon APAC 2023のセッション数
NUMBER_OF_SESSIONS = 5

実行者が入力する仕組み

email_address = input("Eメールアドレスを入力してください: ")
Eメールアドレスを入力してください: 

コードに残さないセキュアな情報の扱い方

他者への共有が望ましくないパスワードなどのセキュアな情報は、漏洩するリスクから、コードでの管理が望ましくありません。実行者に入力させる仕組みにするか、環境変数として管理をすると、その旨を伝えられます。

実行者が入力する仕組み(入力内容をマスク)

from getpass import getpass
password = getpass("パスワードを入力してください: ")
パスワードを入力してください: 

環境変数として管理

import os
password = os.environ.get("PASSWORD")

意図を伝えるための変数と定数の定義

変数や定数を定義する際は、型ヒントやデータクラスにより、どんな内容を意図しているのかを示すことができます。

型ヒントによる意図の表現

conference_name: str = "PyCon APAC"
conference_year: int = 2023

データクラスによる意図の表現

from dataclasses import dataclass
@dataclass
class Conference:
    name: str
    year: int
conference = Conference(name="PyCon APAC", year=2023)

マジックナンバーの防止

マジックナンバーになり得る値は列挙型で定義をしておくことで、以降のコードにおいてマジックナンバーを防止することができます。

列挙型によるマジックナンバーになり得る値の定義

from enum import Enum, unique
@unique # 重複を防止
class Prefecture(Enum): # ※一部省略
    TOKYO = 13
    OSAKA = 27
print(f"PyCon APAC 2023の開催地:{Prefecture(13).name}")

後続処理への影響を避けるバリデーション

後続処理が期待していない値を検知するために、可能な限り早期にバリデーションをすると良いです。また、データのバリデーションを実現するライブラリであるpydanticを活用することで、パラメータや変数(定数)を容易かつ早期に検知することが可能です。

早期のバリデーション

FIRST_YEAR = 2010
assert conference_year > FIRST_YEAR

早期のバリデーション(pydantic)

from pydantic.dataclasses import dataclass, Field
FIRST_YEAR = 2010
@dataclass
class Conference:
    name: str
    year: int = Field(ge=FIRST_YEAR)
conference = Conference(name="PyCon APAC", year=2023)

関数の振る舞いを伝える簡易なテスト

doctestを使うことで、関数の振る舞い伝える簡易なテストの実現ができます。それに加えて、テストであることによって関数が意図した振る舞いであることを担保できます。

doctestを使った簡易なテスト

def add_year(year):
    '''
    >>> add_year(2023)
    2024
    '''
    return year + 1
import doctest
doctest.run_docstring_examples(add_year, globals())

セル内の冪等性を担保した変数の扱い方

特定のセルを複数回実行した場合であっても、後続処理に意図した値を伝えるために、セル内の冪等性を担保した変数の扱い方をすると良いです。例えば、変数を使いまわさない(変数の再代入をしない)ことが挙げられます。

変数を使いまわさない(変数の再代入をしない)

year = 2023
# year = year + 1
next_year = year + 1 # 冪等
# print(year)
print(next_year) = year + 1 # 冪等

その他

  • テキスト(Markdown)のセルによる説明の追加
  • 適度なセルの分割
  • 適度なファイルへの分割
  • フォーマッターの導入
  • リンターの導入
  • Gitの導入
  • 「すべてのセルを実行」で問題なく実行されることを確認
  • CI(継続的インテグレーション)の導入
  • コードレビューの実施(意図が伝えられていない箇所の洗い出し)
  • 「出力をすべて消去」した上で管理(オプション)