Databricksで簡単なファイル入出力を行うアプリケーションを作成する

ユーザー部門の方からCSVファイルをアップロードしてもらって、それに対して処理を行い、結果を返すというユースケースは結構多いと思います。

こちらで紹介しているように、処理を行う機械学習モデルをMLflowモデルサービングで動作させ、UIの画面をStreamlitで構築するという構成を組むことは可能ですが、モデルとMLflowの連携やStreamlitへの処理の組み込みなど、それなりに敷居は高いです。

https://qiita.com/taka_yayoi/items/450b363c6d10475ea0f0

そこで、こちらではアップロードする人がDatabricksにアクセスできるという前提のもとで、簡単にファイルの授受を行えるアプリケーションをDatabricks上で構築する手順を説明します。

全体としては、ユーザー部門の方が以下の操作を行う流れとなります。

1. Databricksノートブックの画面から、処理対象のCSVファイルをDatabricksファイルシステムにアップロード
1. 処理を行うノートブックをジョブとして実行
1. 処理結果のファイルをノートブックからダウンロード

:::note info
**注意**
こちらで紹介しているアプリケーションは社内でのみ活用するプロトタイプと捉えてください。実際にプロダクションに移行するには、セキュリティ設計をしっかり行い、必要に応じてクラウドのネイティブサービスの活用も検討してください。
:::

# ファイルのアップロード(ユーザー部門)

ノートブックの画面からファイルをアップロードします。以下ではファイルアップロードおよび処理実行の手順を説明しています。

- アップロードするCSVファイルが以下の構造になっていることを確認してください。

|id|target|
|:--|:--|
|1|10|

- 画面上部にあるメニューから**ファイル > データをアップロード**を選択します。
![](https://sajpstorage.blob.core.windows.net/yayoi/file_upload_1.png)
- 以下のダイアログの**DBFSターゲットディレクトリ**で、下のCmd5で指定されている`upload_path`の場所を選択します。
![](https://sajpstorage.blob.core.windows.net/yayoi/file_upload_2.png)
- アップロードするファイルを**ファイル**にドラッグアンドドロップします。
![](https://sajpstorage.blob.core.windows.net/yayoi/file_upload_3.png)
- **次へ**をクリックします。
- **完了**をクリックします。

# ノートブックの実装(開発者)

## アップロードされたファイルの処理

以下の例ではpandasで処理を行います。もちろん、Sparkなど任意の処理を記述することができます。

```py:Python
# CSVがアップロードされるDBFS上のパス
upload_path = "/FileStore/shared_uploads/takaaki.yayoi@databricks.com/csv"
# 処理結果を格納するDBFS上のパス
output_path = "/FileStore/shared_uploads/takaaki.yayoi@databricks.com/output"
```

```py:Python
# アップロードパスのクリーンアップ
# アップロード先をクリアする際には以下のコメントを解除して実行してください。
#dbutils.fs.rm(upload_path, True)

# 出力パスのクリーンアップ
# 出力先をクリアする際には以下のコメントを解除して実行してください。
#dbutils.fs.rm(output_path, True)
```

```py:Python
# 上のパスで指定されているディレクトリが存在しない場合には作成します
dbutils.fs.mkdirs(upload_path)
dbutils.fs.mkdirs(output_path)

# ローカルファイルAPIであるpandasからアクセスするので、パスの先頭に/dbfsをつけます
pandas_upload_path = f"/dbfs{upload_path}"
pandas_output_path = f"/dbfs{output_path}"

print("アップロードパス(DBFS):", upload_path)
print("アップロードパス(pandas):", pandas_upload_path)
print("出力ファイルパス(DBFS):", output_path)
print("出力ファイルパス(pandas):", pandas_output_path)

# Databricks Utilityを使ってファイルを確認します
display(dbutils.fs.ls(upload_path))
```
![Screen Shot 2022-08-22 at 19.35.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/8e9efcb7-1f2b-bcf5-ec7a-fc8b21cc519c.png)

```py:Python
import pandas as pd
import glob
import os

# 対象ディレクトリ配下のCSVファイルを対象とします
all_files = glob.glob(os.path.join(pandas_upload_path , "*.csv"))

# 上記ディレクトリ配下にあるすべてのCSVを読み込んで結合します。
df = pd.concat((pd.read_csv(f) for f in all_files), ignore_index=True)

# 読み込んだデータの確認
display(df)

# データ処理
# こちらではtargetの値を2倍にしています
df['target'] = df['target'] * 2

# 処理結果を確認
display(df)
```
![Screen Shot 2022-08-22 at 19.36.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/fa04cd96-f766-a8cd-866f-f43fd5c3ce6a.png)

## 処理結果の出力

```py:Python
import datetime
# ファイル名にタイムスタンプを埋め込みます
# デフォルトのタイムゾーンはUTCなのでJSTを指定します
dt_now_jst = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9)))
output_file_name = dt_now_jst.strftime('%Y%m%d_%H%M%S') + '.csv'

output_file_path = os.path.join(pandas_output_path , output_file_name)

print("出力ファイル:", output_file_path)

df.to_csv(output_file_path)
```
![Screen Shot 2022-08-22 at 19.36.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/29820e5d-be91-560a-0783-3148fdcb250b.png)

```
# Databricks Utilityを使ってファイルを確認します
display(dbutils.fs.ls(output_path))
```
![Screen Shot 2022-08-22 at 19.37.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/a4d7607d-7e3a-9bbd-814f-4616493bd2f4.png)

## ダウンロードリンクの生成

```py:Python
import re

# FileStoreをfilesに置き換えます
download_url_path = re.sub("FileStore", "files", output_file_path)
# 先頭の/dbfsを除外します
download_url_path = re.sub("^/dbfs", "", download_url_path)
print(download_url_path)
```
![Screen Shot 2022-08-22 at 19.36.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/52011151-fad6-1d73-f733-11c9f875841f.png)

```py:Python
displayHTML (f"""
以下のリンクから処理結果ファイルをダウンロードしてください。<br>
<a href='{download_url_path}'>{output_file_name}</a>
""")
```
![Screen Shot 2022-08-22 at 19.38.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/b3d67288-838d-20ef-f6fa-b17cc4837296.png)

# ノートブックをジョブとして登録(開発者)

ノートブック作成者以外の方でも実行できるようにジョブとして登録します。ここでは、エンドユーザーの方にはあまりプログラミングの経験がないことを想定して、可能な限りノートブックの処理を表に出さないようにしています。

1. 画面右上の**スケジュール**をクリックします。
![Screen Shot 2022-08-22 at 19.19.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/8276c043-852d-bab4-7f98-f7e79547b5c6.png)
1. **手動**を選択して**作成**をクリックします。
1. もう一度右上の**スケジュール**をクリックします。
1. **ジョブに表示**をクリックすると別タブでジョブ画面が開きます。この際、ブラウザで表示されるURLをメモしておきます。

:::note info
**注意**
- この時点でジョブの設定を変更することができます。**Compute**ではジョブの実行に使用するクラスターを設定することができますが、デフォルトでは新規にジョブクラスターを作成して実行するのでジョブの起動に数分の時間を要します。常時稼働のクラスターがある場合にはそれを指定することで、ジョブの実行時間を短縮することができます。
- ジョブを実行させたいユーザーに対して実行権限`Can Manage Run`を与えてください。
![Screen Shot 2022-08-22 at 19.53.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7ee288af-2358-60aa-9630-fa1d8cd6b192.png)
- ノートブックのコードを非表示にしたい場合には、セルの右上の下向き矢印をクリックして、メニューから**コードを非表示**を選択します。
![Screen Shot 2022-08-22 at 19.56.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/8c1454ee-e91f-5c0a-e943-7a59cb095e40.png)

:::

# ジョブ実行手順を説明(開発者)

以下のようなドキュメントをユーザー部門の方に共有します。

```md:Markdown
- `<a href="<上記ジョブのURL>" target="_blank">こちらのリンク</a>`をクリックし、ジョブを開いてください。
- **今すぐ実行**をクリックしてください。
![](https://sajpstorage.blob.core.windows.net/yayoi/job_execute_1.png)
- ジョブの結果が**Succeeded**になったことを確認したら**開始時刻**のリンクをクリックして結果にアクセスしてください。
![](https://sajpstorage.blob.core.windows.net/yayoi/job_execute_3.png)
- ノートブックの末尾に表示されているリンクをクリックしてCSVファイルをダウンロードしてください。
![](https://sajpstorage.blob.core.windows.net/yayoi/job_execute_4.png)
```

レンダリングすると以下のようになります。以下のリンクはダミーです。URLを適宜変更してください。

- <a href="<上記ジョブのURL>" target="_blank">こちらのリンク</a>をクリックし、ジョブを開いてください。
- **今すぐ実行**をクリックしてください。
![](https://sajpstorage.blob.core.windows.net/yayoi/job_execute_1.png)
- ジョブの結果が**Succeeded**になったことを確認したら**開始時刻**のリンクをクリックして結果にアクセスしてください。
![](https://sajpstorage.blob.core.windows.net/yayoi/job_execute_3.png)
- ノートブックの末尾に表示されているリンクをクリックしてCSVファイルをダウンロードしてください。
![](https://sajpstorage.blob.core.windows.net/yayoi/job_execute_4.png)


# ジョブの実行(ユーザー部門)

- 上の手順に沿ってジョブを実行します。
- ノートブック末尾に表示されるリンクをクリックしてCSVファイルをダウンロードします。
![Screen Shot 2022-08-22 at 19.49.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/af19ee30-ab69-5ba3-7c15-5ff8aa01d38b.png)
- CSVを開くと処理が行われていることを確認できます。
![Screen Shot 2022-08-22 at 19.28.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/76a61995-cda3-5a99-e21f-7045d7acc754.png)


### Databricks 無料トライアル

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