Databricks Appsを触ってみた

## はじめに
この記事は[Databricks Advent Calendar 2024](https://qiita.com/advent-calendar/2024/databricks)シリーズ1の19日目の記事です。

## Databricks Appsとは
https://docs.databricks.com/ja/dev-tools/databricks-apps/index.html

Databricks Appsは、Databricks上でデータアプリケーションを開発、実行、共有できる機能です。Databricks上で動作するため、追加のインフラストラクチャを構築することなく、データアプリケーションの開発や共有が可能です。

類似の機能としてSnowflakeのStreamlit in Snowflakeがありますが、Databricks AppsではStreamlitの他にもDash、Flask、Gradio、Shinyといったさまざまなフレームワークを利用してアプリケーションを開発できます。

:::note info
パブリックプレビュー
2024年12月時点ではプレビュー版のため利用可能なリージョンが限られています。
Databricks Appsを利用可能なリージョンについては[公式ドキュメント](https://docs.databricks.com/ja/resources/feature-region-support.html)をご参照ください。
:::

## データアプリの作成
新規からアプリを選択します

![スクリーンショット 2024-12-17 18.51.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/7e386c23-9e99-a4b1-fc92-973fc05c5783.png)

Databricks Appsではテンプレートを使ってのアプリ作成も可能です。
テンプレートを使用する場合は、使用するフレームワークと作成するアプリの種類を選択します。

今回はフレームワークにStreamlit、アプリはData appを選択します。

![スクリーンショット 2024-12-17 19.03.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/cf40a62f-4e6c-f5d8-2d1f-80f2b0d07cde.png)

Data appではSQLウェアハウスが必要なため、アプリ名と使用するSQLウェアハウスを選択します。

![スクリーンショット 2024-12-17 19.08.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/81856f56-9dda-b760-0dc7-1367c35af5f9.png)

[アプリを作成してデプロイ]をクリックするとデプロイが始まります。

![スクリーンショット 2024-12-17 19.13.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/d87e6a2f-c84f-a1da-9faa-f2dd5bf92ade.png)

デプロイが完了すると画面の表示が実行中に切り替わります。

![スクリーンショット 2024-12-17 19.18.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/da146051-9288-ac89-e3d9-d712cb417497.png)

実行中のリンクをクリックするとアプリが別タブで起動します。
テンプレートとして提供されているのは、タクシーの乗り場と行き先の郵便番号を指定することで料金を予測するアプリケーションのようです。

![スクリーンショット 2024-12-17 19.21.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/745c8b82-a6a5-ae03-be7b-df6a8ae28603.png)

### Databricks上のテーブルの読み込み
次にコードを修正して、Databricks上に格納されたテーブルにアクセスします。
下記の店舗の売り上げに関するダミーデータを使用し、時系列での売り上げの推移を確認します。

![スクリーンショット 2024-12-18 11.56.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/4dbe0543-d470-12b8-1555-8d883d965939.png)

デプロイメントのソースのリンクをクリックするとデプロイされているアプリのソースコードを確認することができます。

![スクリーンショット 2024-12-17 19.25.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/333e1684-1276-53be-a917-a807e725727a.png)

![スクリーンショット 2024-12-17 19.28.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/67c52c37-7304-639c-c1d1-480ec1b72d63.png)

app.pyを確認すると`getData()`関数でDatabricks上のテーブルからデータを取得しているようです。このクエリを先ほどの売り上げテーブルからデータを取得するクエリに変更します。

```diff_python:app.py
import os
from databricks import sql
from databricks.sdk.core import Config
import streamlit as st
import pandas as pd

# Ensure environment variable is set correctly
assert os.getenv('DATABRICKS_WAREHOUSE_ID'), "DATABRICKS_WAREHOUSE_ID must be set in app.yaml."

def sqlQuery(query: str) -> pd.DataFrame:
    cfg = Config() # Pull environment variables for auth
    with sql.connect(
        server_hostname=cfg.host,
        http_path=f"/sql/1.0/warehouses/{os.getenv('DATABRICKS_WAREHOUSE_ID')}",
        credentials_provider=lambda: cfg.authenticate
    ) as connection:
        with connection.cursor() as cursor:
            cursor.execute(query)
            return cursor.fetchall_arrow().to_pandas()

st.set_page_config(layout="wide")

@st.cache_data(ttl=30)  # only re-query if it's been 30 seconds
def getData():
    # This example query depends on the nyctaxi data set in Unity Catalog, see https://docs.databricks.com/en/discover/databricks-datasets.html for details
-    return sqlQuery("select * from samples.nyctaxi.trips limit 5000")
+    return sqlQuery("select * from select * from qiita_sample.data_apps.sample_sales")

data = getData()

st.header("Taxi fare distribution !!! :)")
col1, col2 = st.columns([3, 1])
with col1:
    st.scatter_chart(data=data, height=400, width=700, y="fare_amount", x="trip_distance")
with col2:
    st.subheader("Predict fare")
    pickup = st.text_input("From (zipcode)", value="10003")
    dropoff = st.text_input("To (zipcode)", value="11238")
    d = data[(data['pickup_zip'] == int(pickup)) & (data['dropoff_zip'] == int(dropoff))]
    st.write(f"# **${d['fare_amount'].mean() if len(d) > 0 else 99:.2f}**")
```

さらに`st.header()`以降の記述も以下の売り上げテーブルの可視化を行うコードに修正します。

```python
st.title('サンプルデータの読み込みと可視化')
st.line_chart(
    data=data,
    x='date',
    y='total_sales'
)

st.dataframe(data)
```

コードの修正が完了したら、先ほどのアプリの画面に戻り、右上の[デプロイ]をクリックします。

![スクリーンショット 2024-12-18 11.48.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/659f74c0-3d77-7390-d55e-cea0eda046bb.png)

デプロイ完了後、アプリを開き直すと更新されていることが確認できます。

![スクリーンショット 2024-12-17 19.41.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/237c0597-366f-5e5b-9e7d-2c14dbfe60b5.png)

### ローカル環境での開発とデプロイ
Databricks AppsではDatabricks上で作成したアプリをローカルで編集し、デプロイすることも可能です。

:::note info
Databricks CLI
ローカルで開発、デプロイを行うためにはPython環境およびDatabricks CLIのインストールが必要です。
https://docs.databricks.com/ja/dev-tools/cli/install.html
:::

以下のコマンドでローカル環境にアプリのソースコードをコピーします。

```terminal
$ databricks workspace export-dir <your_data_app_srouce_path> .
```

ローカル環境にファイルがコピーされました。

![スクリーンショット 2024-12-19 8.32.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/ca04081d-d840-88a0-be93-6ffdef375a2f.png)


以下のコマンドを実行すると、ローカルでの編集内容がリアルタイムにDatabricksへ同期されます。

```terminal
$ databricks sync --watch . <your_data_app_srouce_path>
```

データアプリのタイトルとグラフの種類を折れ線グラフから棒グラフに変更します。

```diff_python:app.py
-st.title('サンプルデータの読み込みと可視化')
-st.line_char(
+st.title('ローカルで編集')
+st.bar_chart(
    data=data,
    x='date',
    y='total_sales'
)

st.dataframe(data)

```

修正が完了したら以下のコマンドでDatabricksアプリへのデプロイを行います。

```terminal
$ databricks apps deploy <app_name> --source-code-path <your_data_app_srouce_path>
```

Databricksでアプリを確認するとローカルでの変更が反映されていることが確認できました。

![スクリーンショット 2024-12-19 9.10.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/6f6fd5ef-430a-280b-c6d9-dd850a1ef546.png)

### アプリの停止

最後に、アプリは起動したままだと課金されるため、不要なアプリは停止 or 削除しておきましょう。画面右上の3点リーダーから操作可能です。

![スクリーンショット 2024-12-19 9.19.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3932340/a50e53cf-1989-ec00-a2fe-6c544e546037.png)