Databricksノートブックによるソフトウェアエンジニアリングのベストプラクティスのウォークスルー

[Software engineering best practices for notebooks \| Databricks on AWS](https://docs.databricks.com/notebooks/best-practices.html) [2022/6/15時点]の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

本書では、お使いのDatabrikcsノートブックに対して、バージョン管理、コード共有、テスト、オプションとして継続的インテグレーション、継続的開発、デリバリー(CI/CD)を含むソフトウェアエンジニアリングのベストプラクティスをどのように適用するのかを説明するハンズオンのウォークスルーを提供します。

このウォークスルーでは以下のことを行います。

- バージョン管理のためにノートブックをDatabricks Reposに追加
- ノートブックの一つから共有可能なモジュールを作成するためにコードの一部を抽出
- 共有コードのテスト
- Databricks Jobsからノートブックの実行
- オプションとして、共有コードにCI/CDを適用

# 要件

このウォークスルーを完了するには以下のリソースが必要です。

- [Databricks ReposによるGit連携](https://qiita.com/taka_yayoi/items/b89f199ff0d3a4c16140)の[要件](https://qiita.com/taka_yayoi/items/b89f199ff0d3a4c16140#%E8%A6%81%E4%BB%B6)を満たすGitプロバイダーのリポジトリ。本書のウォークスルーではGitHubを使用します。このウォークスルーでは、皆様が`best-notebooks`という名前のGitHubリポジトリを持っていることを前提としています。(リポジトリに別の名前をつけることができます。)持っていない場合には[GitHubのリポジトリを作成](https://docs.github.com/en/get-started/quickstart/create-a-repo#create-a-repository)してください。
- Databricksの[ワークスペース](https://qiita.com/taka_yayoi/items/dd41cd715aca272ff5ce)。お持ちでない場合には[ワークスペースを作成](https://docs.databricks.com/administration-guide/account-settings-e2/workspaces.html)してください。
- ワークスペース上のDatabricks [all-purposeクラスター](https://qiita.com/taka_yayoi/items/c5d99cd77fe4bfcf69f0)。設計フェーズでノートブックを実行するために、[稼働中のall-purposeクラスターにノートブックをアタッチ](https://qiita.com/taka_yayoi/items/c306161906d6d34e8bd5#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E3%81%AB%E3%83%8E%E3%83%BC%E3%83%88%E3%83%96%E3%83%83%E3%82%AF%E3%82%92%E3%82%A2%E3%82%BF%E3%83%83%E3%83%81%E3%81%99%E3%82%8B)します。あとで、このウォークスルーではこのクラスター上でのノートブックの実行を自動化するためにDatabricks[ジョブ](https://qiita.com/taka_yayoi/items/b3275a1983c51a8bbe1a)を使用します。(また、ジョブの生存期間中のみ存在する[ジョブクラスター]((https://qiita.com/taka_yayoi/items/c5d99cd77fe4bfcf69f0))でジョブを実行することもできます。)お持ちでない場合には、[all-purposeクラスターを作成](https://qiita.com/taka_yayoi/items/d36a469a1e0c0cebaf1b)してください。

:::note info
**注意**
Databricks Reposでファイルを操作するためには、使用するクラスターではDatabricksランタイム8.4以降がインストールされている必要があります。これらのクラスターにおいては、Databricksランタイム10.4LTSのような最新の長期サポート(LTS)バージョンをインストールすることをお勧めします。
:::

# ウォークスルー

このウォークルスルーでは、以下のことを行います。

1. 既存のGitHubリポジトリをDatabricks Reposに接続します。
1. 既存のノートブックをリポジトリ(repo)に追加し、一回目としてノートブックを実行します。
1. ノートブックのいくつかのコードを共有モジュールに移行します。期待通りにノートブックが共有コードを呼び出すことを確認するために、二回目のノートブックの実行を行います。
1. 最初のノートブックを再度実行することなしに、共有コードを別にテストするために2つ目のノートブックを使用します。
1. 2つのノートブックをオンデマンドあるいは定期的なスケジュールで自動で実行するおゆにDatabrikcsジョブを作成します。
1. リポジトリでプルリクエストが作成された際に、共有コードをテストする2番目のノートブックを実行するようにリポジトリを設定します。
1. 自動でテストを起動する共有コードを変更するプルリクエストを作成します。

以下のステップでは、これらそれぞれのアクティビティをウォークスルーします。

**ステップ**

- [ステップ 1: Databricks Reposのセットアップ](#ステップ-1-databricks-reposのセットアップ)
- [ステップ 2: ノートブックのインポート及び実行](#ステップ-2-ノートブックのインポート及び実行)
- [ステップ 3: コードを共有モジュールに移行](#ステップ-3-コードを共有モジュールに移行)
- [ステップ 4: 共有コードのテスト](#ステップ-4-共有コードのテスト)
- [ステップ 5: ノートブックを実行するジョブの作成](#ステップ-5-ノートブックを実行するジョブの作成)
- [(オプション)ステップ 6: コードの変更があった際に自動でコードをテストしノートブックを実行するようにリポジトリをセットアップ](#オプションステップ-6-コードの変更があった際に自動でコードをテストしノートブックを実行するようにリポジトリをセットアップ)
- [(オプション)ステップ 7: テストを起動するためにGitHubの共有コードを更新](#オプションステップ-7-テストを起動するためにgithubの共有コードを更新)

## ステップ 1: Databricks Reposのセットアップ

このステップでは、既存のDatabricksワークスペースのDatabricks Reposに既存のGitHubのリポジトリを接続します。

お使いのワークスペースからGitHubリポジトリに接続できるようにするためには、まだ設定していない場合には最初にワークスペースにGitHubの認証情報を提供する必要があります。

### ステップ 1.1: GitHub認証情報の提供

1. ワークスペースの[サイドバー](https://qiita.com/taka_yayoi/items/dd41cd715aca272ff5ce#%E3%82%B5%E3%82%A4%E3%83%89%E3%83%90%E3%83%BC%E3%82%92%E4%BD%BF%E3%81%86)の**Data Science & Engineering**あるいは**Databricks Machine Learning**環境で、**Settings > User Settings**をクリックします。
1. **User Settings**ページで**Git integration**をクリックします。
1. **Git integration**タブの**Git provider**で**GitHub**を選択します。
1. **Git provider username or email**に、GitHubのユーザー名を入力します。
1. **Token**には[GitHubのパーソナルアクセストークン](https://docs.github.com/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)を入力します。トークンには**repo**の権限が含まれている必要があります。
1. **Save**をクリックします。

### ステップ 1.2: GitHubリポジトリへの接続

1. サイドバーの**Data Science & Engineering**あるいは**Databricks Machine Learning**環境で、**Repos**をクリックします。
1. **Repos**ペインで**Add Repo**をクリックします。
    1. **Clone remote Git repo**をクリックします。
    1. **Git repository URL**に、お使いのGitHubリポジトリのGitHubの[Clone with HTTPS](https://docs.github.com/repositories/creating-and-managing-repositories/cloning-a-repository)のURLを入力します。本書では、URLの最後が`best-notebooks.git`であることを前提としています。例えば、`https://github.com/<your-GitHub-username>/best-notebooks.git`となります。
    1. **Git repository URL**の隣のドロップダウンで**GitHub**を選択します。
    1. お使いのリポジトリの名前に設定される**Repo name**はそのままにしておきます。例えば、`best-notebooks`となります。
    1. **Create**をクリックします。

## ステップ 2: ノートブックのインポート及び実行

このステップでは、お使いのリポジトリに既存の外部ノートブックをインポートします。このウォークスルーのために自身のノートブックを作成することができますが、クイックに物事を進められるように、こちらではノートブックを準備しています。

### ステップ 2.1: リポジトリで作業用ブランチを作成

このサブステップでは、リポジトリ上に`eda`という名前のブランチを作成します。このブランチを用いることで、ソフトウェアエンジニアリングのベストプラクティスに従い、リポジトリの`main`ブランチとは独立してファイルやコードを操作することができます。(ブランチに別の名前をつけることもできます)

1. **Repos**ペインが表示されていない場合、サイドバーの**Data Science & Engineering**あるいは**Databricks Machine Learning**環境で、**Repos**をクリックします。
1. **Repos**ペインに上のステップで接続したリポジトリが表示されていない場合、あなたのワークスペース上のユーザー名を選択し、以前のステップで接続したリポジトリ名を選択します。
1. リポジトリ名の隣のドロップダウンの矢印をクリックし、**Git**をクリックします。
1. **best-notebooks**ダイアログで **+ (Create branch)** ボタンをクリックします。

    > **注意**
    リポジトリの名前が`best-notebooks`ではない場合、ダイアログのタイトルやこのウォークスルーを通じて表示される内容は異なります。

1. `eda`を入力しEnterを押します。
1. ダイアログを閉じます。

### ステップ 2.2: リポジトリにノートブックをインポート

このサブステップでは、別のリポジトリからお使いのリポジトリに既存のノートブックをインポートします。このノートブックは以下の処理を行います。

1. ワークスペース上のクラスターに[owid/covid\-19\-data](https://github.com/owid/covid-19-data) GitHubリポジトリからCSVファイルをコピーします。このCSVファイルには世界中のCOVID-19による入院患者数、集中治療のメトリクスに関する公開データが含まれています。
1. CSVファイルの中身をpandasデータフレームに読み込みます。
1. アメリカのメトリクスのみを含むようにデータをフィルタリングします。
1. データのグラフを表示します。
1. pandasデータフレームをPandas API on Sparkデータフレームとして保存します。
1. Pandas API on Sparkデータフレームに対するデータクレンジングを実行します。
1. Pandas API on SparkデータフレームをワークスペースにDeltaテーブルとして保存します。
1. Deltaテーブルの中身を表示します。

ここで、リポジトリに自分でノートブックを作成することもできますが、ウォークスルーのスピードを上げるためにここでは既存のノートブックをインポートします。このブランチでノートブックを作成する、ノートブックをインポートするのではなくこのブランチに既存のノートブックを移動するには、[Databricks Reposにおけるノートブックやプロジェクトファイルの取り扱い](https://docs.databricks.com/repos/work-with-notebooks-other-files.html)をご覧ください。

1. リポジトリの**Repos**ペインで、リポジトリ名の隣のドロップダウンの矢印をクリックし、**Create > Folder**をクリックします。
1. **New Folder Name**ダイアログで`notebooks`と入力し、**Create Folder**をクリックします。
1. **Repos**ペインで、リポジトリの名前をクリックし、**notebooks**フォルダの隣のドロップダウンの矢印をクリックし、**Import**をクリックします。
1. **Import Notebooks**ダイアログで、
    1. **Import from**では、**URL**を選択します。
    1. GitHubの`databricks/notebook-best-practices`リポジトリの`covid_eda_raw`ノートブックの生のコンテンツのURLを入力します。URLを取得するには、
        1. https://github.com/databricks/notebook-best-practices に移動します。
        1. `notebooks`フォルダをクリックします。
        1. `covid_eda_raw.py`ファイルをクリックします。
        1. **Raw**をクリックします。
        1. ブラウザのアドレスバーに表示される完全なURLをコピーし、**Import Notebooks**ダイアログに貼り付けます。
   1. **Import**をクリックします。

### ステップ 2.3: ノートブックの実行

1. ノートブックが表示されていない場合、お使いのリポジトリの**Repos**ペインで**notebooks**フォルダーの中にある**covid_eda_raw**ノートブックをダブルクリックして開きます。
1. ノートブックでは、**File**の隣のドロップダウンリストから、このノートブックをアタッチするクラスターを選択します。
1. **Run All**をクリックします。
1. プロンプトが表示された場合には、**Attach & Run**あるいは**Start, Attach & Run**をクリックします。
1. ノートブックの処理が終わるのを待ちます。

ノートブックが処理を終えると、ノートブック上でデータのグラフを確認でき、Deltaテーブルには600行以上の生データが格納されます。このノートブックの実行を開始した際、クラスターが起動していない場合、結果を表示するまでに数分かかることがあります。

### ステップ 2.4: ノートブックのチェックイン、マージ

このサブステップでは、これまでの作業内容をGitHubのリポジトリに保存します。そして、作業用ブランチからリポジトリの`main`ブランチにノートブックをマージします。

1. リポジトリの**Repos**ペインで**eda**ブランチをクリックします。
1. **best-notebooks**ダイアログの**Changes**タブで、**notebooks/covid_eda_raw.py**ファイルが選択されていることを確認します。
1. **Summary (required)** には`Added raw notebook`と入力します。
1. **Description (optional)** には`This is the first version of the notebook`と入力します。
1. **Commit & Push**をクリックします。
1. **History**をクリックし、ポップアップの**Create a pull request on your git provider**リンクをクリックします。
1. GitHubで、**Pull requests**タブをクリックし、プルリクエストを作成し、プルリクエストを`main`ブランチにマージします。
1. Databricksワークスペースに戻り、**best-notebooks**ダイアログが表示されている場合には閉じます。

## ステップ 3: コードを共有モジュールに移行

このステップでは、ノートブックのいくつかのコードを、ノートブックから一連の共有関数に移動します。これによって、これらの関数を他の同様のノートブックから使用することができ、今後のコーディングを加速し、より予測可能で一貫性のあるノートブック結果を保証する助けとなります。また、このコードを共有することで、これらの関数をより簡単にテストできるようになり、今後もこのコードの全体的な品質を高めるソフトウェアエンジニアリングのベストプラクティスに従うことができます。

### ステップ 3.1: リポジトリで別の作業用ブランチを作成

1. ワークスペースで、お使いのリポジトリの**Repos**ペインで**eda**ブランチをクリックします。
1. **best-notebooks**ダイアログで、**eda**ブランチの隣のドロップダウンの矢印をクリックし、**main**を選択します。
1. **Pull**ボタンをクリックします。プルを実行するプロンプトが表示されたら**Confirm**をクリックます。
1. **+ (Create branch)** ボタンをクリックします。
1. `first_modules`を入力しEnterを押します。(ブランチに別の名前をつけることもできます)
1. ダイアログを閉じます。

### ステップ 3.2: リポジトリにノートブックをインポート

このウォークスルーをスピードアップするために、このサブステップではあなたのリポジトリに既存の別のノートブックをインポートします。このノートブックは、ノートブックの外に保存されている共有コードの関数を呼び出すことを除き、以前のノートブックと同じこと行います。繰り返しになりますが、ここでも自分のリポジトリに自分のコードを作成し、実際のコードを自分で共有することができます。

1. **Repos**ペインで、リポジトリの名前をクリックし、**notebooks**フォルダの隣のドロップダウンの矢印をクリックし、**Import**をクリックします。
1. **Import Notebooks**ダイアログで、
    1. **Import from**では、**URL**を選択します。
    1. GitHubの`databricks/notebook-best-practices`リポジトリの`covid_eda_modular`ノートブックの生のコンテンツのURLを入力します。URLを取得するには、
        1. https://github.com/databricks/notebook-best-practices に移動します。
        1. `notebooks`フォルダをクリックします。
        1. `covid_eda_modular.py`ファイルをクリックします。
        1. **Raw**をクリックします。
        1. ブラウザのアドレスバーに表示される完全なURLをコピーし、**Import Notebooks**ダイアログに貼り付けます。
   1. **Import**をクリックします。

:::note info
**注意**
新しい`covid_eda_modular`ノートブックは最初のノートブックの共有バージョンであるため、この時点で既存の`covid_eda_raw`ノートブックを削除することができます。しかし、以前のノートブックを使わないとしても、比較のために以前のノートブックを保持しておいても構いません。
:::

### ステップ 3.3: ノートブックサポート用の共有コード関数を追加

1. リポジトリの**Repos**ペインで、リポジトリ名の隣のドロップダウンの矢印をクリックして、**Create > Folder**をクリックします。

    > **注意**
**notebooks**フォルダの隣のドロップダウンの矢印をクリックしないでください。代わりにリポジトリ名の隣のドロップダウンの矢印をクリックしてください。**notebooks**フォルダの中ではなく、リポジトリのルートにフォルダを作成します。

1. **New Folder Name**ダイアログで`covid_analysis`を入力し、**Create Folder**をクリックします。
1. リポジトリの**Repos**ペインで、**covid_analysis**の隣のドロップダウンの矢印をクリックして、**Create > File**をクリックします。
1. **New File Name**ダイアログで`transforms.py`を入力し、**Create File**をクリックします。
1. リポジトリの**Repos**ペインで、**covid_analysis**フォルダをクリックし、**transforms.py**をクリックします。
1. エディタウィンドウで以下のコードを入力します。

    ```py:Python
    import pandas as pd
    
    # Filter by country code.
    def filter_country(pdf, country="USA"):
      pdf = pdf[pdf.iso_code == country]
      return pdf
    
    # Pivot by indicator, and fill missing values.
    def pivot_and_clean(pdf, fillna):
      pdf["value"] = pd.to_numeric(pdf["value"])
      pdf = pdf.fillna(fillna).pivot_table(
        values="value", columns="indicator", index="date"
      )
      return pdf
    
    # Create column names that are compatible with Delta tables.
    def clean_spark_cols(pdf):
      pdf.columns = pdf.columns.str.replace(" ", "_")
      return pdf
    
    # Convert index to column (works with pandas API on Spark, too).
    def index_to_col(df, colname):
      df[colname] = df.index
      return df
    ```

:::note info
**ティップス**
他のコード共有のテクニックに関しては、[ノートブックでコードを共有する](https://qiita.com/taka_yayoi/items/dfb53f63aed2fbd344fc#%E3%83%8E%E3%83%BC%E3%83%88%E3%83%96%E3%83%83%E3%82%AF%E3%81%A7%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E5%85%B1%E6%9C%89%E3%81%99%E3%82%8B)をご覧ください。
:::

### ステップ 3.4: 共有コードの依存関係の追加

上のコードでは、コードが適切に動作するためにいくつかのPythonパッケージの依存関係を含んでいます。このサブステップでは、これらのパッケージの依存関係を宣言します。

1. リポジトリの**Repos**ペインで、リポジトリ名の隣のドロップダウンの矢印をクリックして、**Create > File**をクリックします。

    > **注意**
**notebooks**フォルダや**covid_analysis**フォルダの隣のドロップダウンの矢印をクリックしないでください。代わりにリポジトリ名の隣のドロップダウンの矢印をクリックしてください。**notebooks**フォルダや**covid_analysis**フォルダの中ではなく、リポジトリのルートにパッケージ依存関係のリストを作成します。

1. **New File Name**ダイアログで`requirements.txt`と入力し**Create File**をクリックします。
1. リポジトリの**Repos**ペインで、**requirements.txt**をクリックし、以下のコードを入力します。

    > **注意**
`requirements.txt`ファイルが見えない場合、ブラウザをリフレッシュしてください。

    ```
    -i https://pypi.org/simple
    attrs==21.4.0
    cycler==0.11.0
    fonttools==4.33.3
    iniconfig==1.1.1
    kiwisolver==1.4.2
    matplotlib==3.5.1
    numpy==1.22.3
    packaging==21.3
    pandas==1.4.2
    pillow==9.1.0
    pluggy==1.0.0
    py==1.11.0
    py4j==0.10.9.3
    pyarrow==7.0.0
    pyparsing==3.0.8
    pyspark==3.2.1
    pytest==7.1.2
    python-dateutil==2.8.2
    pytz==2022.1
    six==1.16.0
    tomli==2.0.1
    wget==3.2
    ```

:::note info
**注意**
上のファイルは固有のパッケージのバーションを列挙しています。互換性を高めるために、ご自身のall-puporseクラスターにインストールされているバージョンとこれらのバージョンを相互参照させることができます。[Databricks runtime releases](https://docs.databricks.com/release-notes/runtime/releases.html)にあるお使いのDatabricksランタイムバージョンの「System environment」セクションを参照ください。
:::

これで、あなたのリポジトリの構成は以下のようになっているはずです。

```
|-- covid_analysis
|   `-- transforms.py
|-- notebooks
|   |-- covid_eda_modular
|   `-- covid_eda_raw (optional)
`-- requirements.txt
```

### ステップ 3.5: リファクタリングしたノートブックの実行

このサブステップでは、`covid_analysis/transforms.py`にある共有コードを呼び出す`covid_eda_modular`ノートブックを実行します。

1. リポジトリの**Repos**ペインで、**notebooks**フォルダの中にある**covid_eda_modular**ノートブックをダブルクリックします。
1. **File**の隣のドロップダウンリストから、このノートブックをアタッチするクラスターを選択します。
1. **Run All**をクリックします。
1. プロンプトが表示された場合には、**Attach & Run**あるいは**Start, Attach & Run**をクリックします。
1. ノートブックの処理が終わるのを待ちます。

ノートブックが処理を終えると、`covid_eda_raw`ノートブックと同じ結果を確認することができます。ノートブック上でデータのグラフを確認でき、Deltaテーブルには600行以上の生データが格納されます。このノートブックの実行を開始した際、クラスターが起動していない場合、結果を表示するまでに数分かかることがあります。

### ステップ 3.6: ノートブックと関連コードのチェックイン

1. リポジトリの**Repos**ペインで**first_modules**ブランチをクリックします。
1. **best-notebooks**ダイアログの**Changes**タブで以下が選択されていることを確認します。
    - **requirements.txt**
    - **covid_analysis/transforms.py**
    - **notebooks/covid_eda_modular.py**
1. **Summary (required)** には`Added refactored notebook`と入力します。
1. **Description (optional)** には`This is the second version of the notebook`と入力します。
1. **Commit & Push**をクリックします。
1. **History**をクリックし、ポップアップの**Create a pull request on your git provider**リンクをクリックします。
1. GitHubで、**Pull requests**タブをクリックし、プルリクエストを作成し、プルリクエストを`main`ブランチにマージします。
1. Databricksワークスペースに戻り、**best-notebooks**ダイアログが表示されている場合には閉じます。

## ステップ 4: 共有コードのテスト

このステップでは、上のステップの共有コードをテストします。しかし、`covid_eda_modular`ノートブック自身を実行することなしに、このコードをテストしたいと考えています。これは、もし共有コードが実行に失敗するのであれば、まず間違いなくノートブック自身の処理も失敗するためです。後でメインのノートブックが失敗する前に、最初に共有コードで失敗を捕捉したいと考えます。このテストテクニックはソフトウェアエンジニアリングのベストプラクティスです。

### ステップ 4.1: リポジトリで別の作業用ブランチを作成

1. ワークスペースで、お使いのリポジトリの**Repos**ペインで**first_modules**ブランチをクリックします。
1. **best-notebooks**ダイアログで、**first_modules**ブランチの隣のドロップダウンの矢印をクリックし、**main**を選択します。
1. **Pull**ボタンをクリックします。プルを実行するプロンプトが表示されたら**Confirm**をクリックます。
1. **+ (Create branch)** ボタンをクリックします。
1. `first_tests`を入力しEnterを押します。(ブランチに別の名前をつけることもできます)
1. ダイアログを閉じます。

### ステップ 4.2: テストの追加

このサブステップでは、あなたの共有コードをテストするために[pytest](https://docs.pytest.org/)フレームワークを使用します。これらのテストにおいては、特定のテスト結果が達成されたかどうかを[assert](https://docs.pytest.org/en/7.1.x/getting-started.html)します。あるテストが期待しない結果を出した際には、その特定のテストはアサーションに失敗し、テスト自体が失敗します。

1. リポジトリの**Repos**ペインで、リポジトリ名の隣のドロップダウンの矢印をクリックし、**Create > Folder**をクリックします。
1. **New Folder Name**ダイアログで`tests`を入力し、**Create Folder**をクリックします。
1. リポジトリの**Repos**ペインで、**tests**の隣のドロップダウンの矢印をクリックして、**Create > File**をクリックします。
1. **New File Name**ダイアログで`testdata.csv`を入力し、**Create File**をクリックします。
1. リポジトリの**Repos**ペインで、**test**フォルダをクリックし、**testdata.csv**をクリックします。
1. エディタウィンドウで以下のテストデータを入力します。

    ```
    entity,iso_code,date,indicator,value
    United States,USA,2022-04-17,Daily ICU occupancy,
    United States,USA,2022-04-17,Daily ICU occupancy per million,4.1
    United States,USA,2022-04-17,Daily hospital occupancy,10000
    United States,USA,2022-04-17,Daily hospital occupancy per million,30.3
    United States,USA,2022-04-17,Weekly new hospital admissions,11000
    United States,USA,2022-04-17,Weekly new hospital admissions per million,32.8
    Algeria,DZA,2022-04-18,Daily ICU occupancy,1010
    Algeria,DZA,2022-04-18,Daily ICU occupancy per million,4.5
    Algeria,DZA,2022-04-18,Daily hospital occupancy,11000
    Algeria,DZA,2022-04-18,Daily hospital occupancy per million,30.9
    Algeria,DZA,2022-04-18,Weekly new hospital admissions,10000
    Algeria,DZA,2022-04-18,Weekly new hospital admissions per million,32.1
    ```

    > **注意**
テストデータを利用することはソフトウェアエンジニアリングのベストプラクティスです。これによって、本当のデータと同じフォーマットを持つ小規模のデータを用いて、迅速にテストを実行することができます。もちろん、テストを実行する前に、このテストデータが本当のデータを正確に表現していることを常に確認すべきです。

1. リポジトリの**Repos**ペインで、**tests**フォルダの隣のドロップダウンの矢印をクリックして、**Create > File**をクリックします
1. **New File Name**ダイアログで`transforms_test.py`を入力し、**Create File**をクリックします。
1. リポジトリの**Repos**ペインで、**test**フォルダをクリックし、**transforms_test.py**をクリックします。
1. エディタウィンドウで以下のコードを入力します。これらのテストでは、標準的な`pytest`の[fixtures](https://docs.pytest.org/en/7.1.x/explanation/fixtures.html#about-fixtures)とダミーのインメモリpandasデータフレームを使用します。

    ```py:Python
    # Test each of the transform functions.
    import pytest
    from textwrap import fill
    import os
    import pandas as pd
    import numpy as np
    from covid_analysis.transforms import *
    from pyspark.sql import SparkSession
    
    @pytest.fixture
    def raw_input_df() -> pd.DataFrame:
      """
      Create a basic version of the input dataset for testing, including NaNs.
      """
      return pd.read_csv('tests/testdata.csv')
    
    @pytest.fixture
    def colnames_df() -> pd.DataFrame:
      df = pd.DataFrame(
        data=[[0,1,2,3,4,5]],
        columns=[
          "Daily ICU occupancy",
          "Daily ICU occupancy per million",
          "Daily hospital occupancy",
          "Daily hospital occupancy per million",
          "Weekly new hospital admissions",
          "Weekly new hospital admissions per million"
        ]
      )
      return df
    
    # Make sure the filter works as expected.
    def test_filter(raw_input_df):
      filtered = filter_country(raw_input_df)
      assert filtered.iso_code.drop_duplicates()[0] == "USA"
    
    # The test data has NaNs for Daily ICU occupancy; this should get filled to 0.
    def test_pivot(raw_input_df):
      pivoted = pivot_and_clean(raw_input_df, 0)
      assert pivoted["Daily ICU occupancy"][0] == 0
    
    # Test column cleaning.
    def test_clean_cols(colnames_df):
      cleaned = clean_spark_cols(colnames_df)
      cols_w_spaces = cleaned.filter(regex=(" "))
      assert cols_w_spaces.empty == True
    
    # Test column creation from index.
    def test_index_to_col(raw_input_df):
      raw_input_df["col_from_index"] = raw_input_df.index
      assert (raw_input_df.index == raw_input_df.col_from_index).all()
    ```

これで、あなたのリポジトリの構成は以下のようになっているはずです。

```
|-- covid_analysis
|   `-- transforms.py
|-- notebooks
|   |-- covid_eda_modular
|   `-- covid_eda_raw (optional)
|-- requirements.txt
`-- tests
    |-- testdata.csv
    `-- transforms_test.py
```

### ステップ 4.3: テストの実行

このウォークスルーをスピードアップするために、このサブステップでは上述のテストを実行するためにインポートされたノートブックを使用します。このノートブックは、テストが依存するPythonパッケージをダウンロードし、お使いのワークスペースにインストールし、テストを実行し、テストの結果をレポートします。お使いのクラスターの[webターミナル](https://docs.databricks.com/clusters/web-terminal.html)から`pytest`を実行することもできますが、ノートブックから実行した方が便利です。

1. **Repos**ペインで、リポジトリの名前をクリックし、**notebooks**フォルダの隣のドロップダウンの矢印をクリックし、**Import**をクリックします。
1. **Import Notebooks**ダイアログで、
    1. **Import from**では、**URL**を選択します。
    1. GitHubの`databricks/notebook-best-practices`リポジトリの`run_unit_tests`ノートブックの生のコンテンツのURLを入力します。URLを取得するには、
        1. https://github.com/databricks/notebook-best-practices に移動します。
        1. `notebooks`フォルダをクリックします。
        1. `run_unit_tests.py`ファイルをクリックします。
        1. **Raw**をクリックします。
        1. ブラウザのアドレスバーに表示される完全なURLをコピーし、**Import Notebooks**ダイアログに貼り付けます。
   1. **Import**をクリックします。

1. ノートブックが表示されていない場合、リポジトリの**Repos**ペインで**notebooks**フォルダをクリックし、**run_unit_tests**ノートブックをダブルクリックします。
1. **File**の隣のドロップダウンリストからノートブックをアタッチするクラスターを選択します。
1. **Run All**をクリックします。
1. プロンプトが表示された場合には、**Attach & Run**あるいは**Start, Attach & Run**をクリックします。
1. ノートブックの処理が終わるのを待ちます。

ノートブックの処理が終わると、ノートブックには他の関連情報とともに通過したテストの数、失敗したテストの数に関する情報を確認することができます。このノートブックの実行を開始した際、クラスターが起動していない場合、結果を表示するまでに数分かかることがあります。

これで、リポジトリの構造は以下のようになっているはずです。

```
|-- covid_analysis
|   `-- transforms.py
|-- notebooks
|   |-- covid_eda_modular
|   |-- covid_eda_raw (optional)
|   `-- run_unit_tests
|-- requirements.txt
`-- tests
    |-- testdata.csv
    `-- transforms_test.py
```

### ステップ 4.4: ノートブックと関連テストのチェックイン

1. リポジトリの**Repos**ペインで**first_tests**ブランチをクリックします。
1. **best-notebooks**ダイアログの**Changes**タブで以下が選択されていることを確認します。
    - **tests/transforms_test.py**
    - **notebooks/run_unit_tests.py**
    - **tests/testdata.csv**
1. **Summary (required)** には`Added tests`と入力します。
1. **Description (optional)** には`These are the unit tests for the shared code.`と入力します。
1. **Commit & Push**をクリックします。
1. **History**をクリックし、ポップアップの**Create a pull request on your git provider**リンクをクリックします。
1. GitHubで、**Pull requests**タブをクリックし、プルリクエストを作成し、プルリクエストを`main`ブランチにマージします。
1. Databricksワークスペースに戻り、**best-notebooks**ダイアログが表示されている場合には閉じます。

## ステップ 5: ノートブックを実行するジョブの作成

上のステップでは、共有コードを手動でテストし、手動でノートブックを実行しました。このステップでは、オンデマンドあるいは定期的なスケジュールで、自動でノートブックを実行し、共有コードをテストするためにDatabricksジョブを使用します。

### ステップ 5.1: ノートブックのテストを実行するジョブタスクの作成

1. **Data Science & Engineering**あるいは**Databricks Machine Learning**環境のサイドバーで**Workflows**をクリックします。
1. **Jobs**タブで**Create Job**をクリックします。
1. **Add a name for your job**には`covid_report`を入力します。
1. **Task name**には`run_notebook_tests`を入力します。
1. **Type**では**Notebook**を選択します。
1. **Source**では**Git**を選択します。
1. **Add a git reference**をクリックします。
1. **Git information**ダイアログで、
    1. **Git repository URL**にはお使いのGitHubリポジトリのGitHub[Clone with HTTPS](https://docs.github.com/repositories/creating-and-managing-repositories/cloning-a-repository)のURLを入力します。本書では、URLの最後が`best-notebooks.git`であることを前提としています。例えば、`https://github.com/<your-GitHub-username>/best-notebooks.git`となります。
    1. **Git provider**では**GitHub**を選択します。
    1. **Git reference (branch / tag / commit)** には`main`と入力します。
    1. **Git reference (branch / tag / commit)** の隣では**branch**を選択します。
    1. **Confirm**をクリックします。
1. **Path**には`notebooks/run_unit_tests`を入力します。ファイルの拡張子`.py`を含めないでください。
1. **Cluster**には、以前のステップで使用したクラスターを選択します。
1. **Create**をクリックします。

:::note info
**注意**
このシナリオでは、このノートブックを定期的に実行するために、ジョブをスケジュールするために[ノートブックのスケジュール](https://qiita.com/taka_yayoi/items/c306161906d6d34e8bd5#%E3%83%8E%E3%83%BC%E3%83%88%E3%83%96%E3%83%83%E3%82%AF%E3%81%AE%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E5%87%A6%E7%90%86)で説明されているように、ノートブック上のスケジュールボタンを使用することはお勧めしません。これは、スケジュールボタンがワークスペースのリポジトリにあるノートブックの最新の*作業中の*コピーを用いたジョブを作成するためです。そうではなく、レポジトリ上で*コミットされた*最新のノートブックのバージョンを用いたジョブを作成する上述の指示に従うことをお勧めします。
:::

### ステップ 5.2: メインのノートブックを実行するジョブタスクの作成

1. **+ (Add more tasks to your job here)** アイコンをクリックします。
1. **Task name**には`run_main_notebook`を入力します。
1. **Type**では**Notebook**を選択します。
1. **Path**には`notebooks/covid_eda_modular`を入力します。ファイルの拡張子`.py`を含めないでください。
1. **Cluster**には、以前のステップで使用したクラスターを選択します。
1. **Create task**をクリックします。

### ステップ 5.3: ジョブの実行

1. **Run now**をクリックします。
1. ポップアップで**View run**をクリックします。

    > **注意**
すぐにポップアップが消える場合、以下を実施してください。
    > 1. **Data Science & Engineering**あるいは**Databricks Machine Learning**環境のサイドバーで**Workflows**をクリックします。
    > 1. **Job runs**タブで、**Jobs**カラムの**covid_report**の最新のジョブの**Start time**をクリックします。

1. ジョブの結果を確認するには、**run_notebook_tests**タイル、**run_main_notebook**タイル、あるいは両方をクリックします。

:::note info
**注意**
このジョブはオンデマンドです。このジョブを定期的に実行するには[ジョブのスケジュール](https://qiita.com/taka_yayoi/items/b3275a1983c51a8bbe1a#%E3%82%B8%E3%83%A7%E3%83%96%E3%81%AE%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB)を参照ください。
:::

## (オプション)ステップ 6: コードの変更があった際に自動でコードをテストしノートブックを実行するようにリポジトリをセットアップ

以前のステップでは、ある時点、あるいは繰り返しベースで自動的に共有コードをテストし、ノートブックを実行するためにジョブを使用しました。しかし、変更がお使いのGitHubリポジトリにマージされた際に、自動でテストを起動することを好むかもしれません。[GitHub Actions](https://docs.github.com/actions)のようなCI/CDプラットフォームを用いることで、この自動化を実現することができます。

### ステップ 6.1: ワークスペースに対するGitHubアクセスのセットアップ

このサブステップでは、変更がリポジトリにマージされた際にワークスペースでジョブを実行するGitHub Actionsワークフローをセットアップします。アクセスできるようにするために、GitHubにユニークなDatabricksのトークンを指定します。

セキュリティ上の理由から、DatabricksワークスペースのユーザーのパーソナルアクセストークンをGitHubに提供することはお勧めしません。代わりに、GitHubにはDatabricksのサービスプリンシパルに関連づけられているDatabricksのアクセストークンを提供することをお勧めします。手順に関しては、GitHub Actionsマーケットプレースの[Run Databricks Notebook GitHub Action](https://github.com/marketplace/actions/run-databricks-notebook)の[AWS](https://github.com/marketplace/actions/run-databricks-notebook#aws)セクションをご覧ください。

:::note warn
**重要！**
ノートブックは、トークンに関連づけられているアイデンティティのワークスペースのアクセス権を用いて実行されますので、サービスプリンシパルを使うことをお勧めします。個人的な探索目的のみで、かつ、このやり方が非推奨であるセキュリテイ上の理由を理解しているのであれば、DatabricksワークスペースのユーザーのパーソナルアクセストークンをGitHubに提供したい場合は、[ワークスペースのユーザーのパーソナルアクセストークンの作成](https://docs.databricks.com/dev-tools/api/latest/authentication.html#token-management)の手順をご覧ください。
:::

### ステップ 6.2: GitHub Actionsワークフローの追加

このサブステップでは、リポジトリにプルリクエストがあった場合に`run_unit_tests`ノートブックを実行するために、GitHub Actionsワークフローを追加します。

このサブステップでは、お使いのGitHubリポジトリの複数のフォルダ階層に格納されるファイルにGitHub Actionsワークフローを格納します。GitHub Actionsが適切に動作するためには、GitHub Actionsは特定のネストされたフォルダ階層に存在する必要があります。Databricks Reposのユーザーインタフェースでは、ネストされたフォルダ階層を作成できないので、このステップを完了するためにはGitHubリポジトリのウェブサイトを使用する必要があります。

1. GitHubリポジトリのウェブサイトで**Code**をクリックします。
1. **Switch branches or tags**ドロップダウンリストで、選択されていない場合には**main**を選択します。
1. **Switch branches or tags**ドロップダウンリストで、**Find or create a branch**ボックスが表示されていない場合には、再度**main**をクリックします。
1. **Find or create a branch**ボックスでは、`adding_github_actions`を入力します。
1. **Create branch: adding_github_actions from ‘main’** をクリックします。
1. **Add file > Create new file**をクリックします。
1. **Name your file**には、`.github/workflows/databricks_pull_request_tests.yml`を入力します。
1. エディタウィンドウに以下のコードを入力します。このコードは`run_unit_tests`ノートブックを実行するために[Run Databricks Notebook GitHub Action](https://github.com/marketplace/actions/run-databricks-notebook)を使用するためのpull_requestフックを宣言して使用ます。

    下のコードでは、以下を置換してください。
    - `<your-workspace-instance-URL>`をお使いのDatabricks[インスタンス名](https://qiita.com/taka_yayoi/items/7127cabac70dd994fcba#%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E5%90%8Durlid)で置き換え
    - `<your-access-token>`を上で生成したトークンで置き換え
    - `<your-cluster-id>`をターゲットの[クラスターID](https://qiita.com/taka_yayoi/items/7127cabac70dd994fcba#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E3%81%AEurlid)で置き換え

    ```yaml:YAML
    name: Run pre-merge Databricks tests
    
    on:
      pull_request:
    
    env:
      # Replace this value with your workspace instance name.
      DATABRICKS_HOST: https://<your-workspace-instance-name>
    
    jobs:
      unit-test-notebook:
        runs-on: ubuntu-latest
        timeout-minutes: 15
    
        steps:
          - name: Checkout repo
            uses: actions/checkout@v2
          - name: Run test notebook
            uses: databricks/run-notebook@main
            with:
              databricks-token: <your-access-token>
    
              local-notebook-path: notebooks/run_unit_tests.py
    
              existing-cluster-id: <your-cluster-id>
    
              git-commit: "${{ github.event.pull_request.head.sha }}"
    
              # Grant all users view permission on the notebook's results, so that they can
              # see the result of the notebook, if they have related access permissions.
              access-control-list-json: >
                [
                  {
                    "group_name": "users",
                    "permission_level": "CAN_VIEW"
                  }
                ]
              run-name: "EDA transforms helper module unit tests"
    ```

1. **Create a new branch for this commit and start a pull request**を選択します。
1. **Propose new file**をクリックします。
1. **Pull requests**タブをクリックし、プルリクエストを作成します。
1. プルリクエストページで、**Run pre-merge Databricks tests / unit-test-notebook (pull_request)** の隣のアイコンが緑のチェックマークになるのを待ちます。(アイコンが表示されるまでにしばらくの時間を要します)緑のチェックマークではなく X が表示された場合、原因を探るために**Details**をクリックします。アイコンや**Details**が表示されない場合、**Show all checks**をクリックします。
1. 緑のチェックマークが表示されたら、プルリクエストを`main`ブランチにマージします。

## (オプション)ステップ 7: テストを起動するためにGitHubの共有コードを更新

このステップでは、共有コードに変更を加え、変更をGitHubリポジトリにプッシュすることで、上のステップで設定したGitHub Actionsに基づき、即座に自動でテストを起動します。

### ステップ 7.1: リポジトリで別の作業用ブランチを作成

1. ワークスペースで、お使いのリポジトリの**Repos**ペインで**first_tests**ブランチをクリックします。
1. **best-notebooks**ダイアログで、**first_tests**ブランチの隣のドロップダウンの矢印をクリックし、**main**を選択します。
1. **Pull**ボタンをクリックします。プルを実行するプロンプトが表示されたら**Confirm**をクリックます。
1. **+ (Create branch)** ボタンをクリックします。
1. `trigger_tests`を入力しEnterを押します。(ブランチに別の名前をつけることもできます)
1. ダイアログを閉じます。

### ステップ 7.2: 共有コードの変更

1. ワークスペースでお使いのリポジトリの**Repos**ペインで、**covid_analysis/transforms.py**ファイルをダブルクリックします。
1. このファイルの3行目でコードを変更します。

```py:Python
# Filter by country code.
```

を

```py:Python
# Filter by country code. If not specified, use "USA."
```

に変更します。

### ステップ 7.3: テストを起動するために変更をチェックイン

1. リポジトリの**Repos**ペインで**trigger_tests**ブランチをクリックします。
1. **best-notebooks**ダイアログの**Changes**タブで**covid_analysis/transforms.py**が選択されていることを確認します。
1. **Summary (required)** には`Updated comment`と入力します。
1. **Description (optional)** には`This updates the comment for filter_country.`と入力します。
1. **Commit & Push**をクリックします。
1. **History**をクリックし、ポップアップの**Create a pull request on your git provider**リンクをクリックします。
1. GitHubで、**Pull requests**タブをクリックし、プルリクエストを作成し、プルリクエストを`main`ブランチにマージします。
1. プルリクエストページで、**Run pre-merge Databricks tests / unit-test-notebook (pull_request)** の隣のアイコンが緑のチェックマークになるのを待ちます。(アイコンが表示されるまでにしばらくの時間を要します)緑のチェックマークではなく X が表示された場合、原因を探るために**Details**をクリックします。アイコンや**Details**が表示されない場合、**Show all checks**をクリックします。
1. 緑のチェックマークが表示されたら、プルリクエストを`main`ブランチにマージします。

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)