Snowflake Notebooksを試してみよう！

## Snowflake Notebooks

Snowflake NotebooksはSnowflakeのWeb UIであるSnowsight上で提供されている開発環境です。SQL、Python、Markdownを一つのノート上に混在して記述できるのが特徴です。  
互換性があるかは分からないですが、Jupyter Notebookのようなサービスになっています。  

![Snowflake Notebooks](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/6c3b6253-d6a3-0755-a2d4-1fd38048f813.png)

このサービスが先日2024/5/25にPublic Preview (PuPr)されたようです！  

## 事前準備

ノートブックを作成するロールに対して```CREATE NOTEBOOK```権限が必要なようです。例えば以下のようなコマンドを実行すればよさそう。  

```sql
-- 権限があるロールになる
USE ROLE ACCOUNTADMIN;
-- notebook_test.publicにノートブックを作成できる権限をSYSADMINに与える
GRANT CREATE NOTEBOOK ON SCHEMA notebook_test.public TO ROLE SYSADMIN;
-- SYSADMINになる
USE ROLE SYSADMIN;
```

Snowsightをリロードすれば左上のCreate(作成)メニューからノートブックの作成ができるようになっています。  

![Create](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/9d23212f-dc93-5e21-0c00-c95f40df1b25.png)

## 事前準備その2：適当なデータセットを準備する

今回はついでに映画のレーティングに関するデータセット(ml-latest-small.zip)を以下のサイトから取得して、ロードしました。[^1]  
利用規約は[README.html](https://files.grouplens.org/datasets/movielens/ml-latest-small-README.html)をご参照ください。  

https://grouplens.org/datasets/movielens/  

GUIからロードする場合は最初の行をヘッダとして使うように設定すれば問題なくロードされるはずです。  

![Load MovieLens Dataset](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/2c1915ba-2767-61f9-65db-f979a9271649.png)

[^1]: F. Maxwell Harper and Joseph A. Konstan. 2015. The MovieLens Datasets: History and Context. ACM Transactions on Interactive Intelligent Systems (TiiS) 5, 4: 19:1–19:19. https://doi.org/10.1145/2827872

## ノートブックの操作

Snowsightからノートブックを作成するとサンプルが入力された状態で始まるので雰囲気で動かせると思いますが、以降いくつかメモしておきます。  

### セッションの取得

セッションはPythonセルからデータベースを参照するために必要となります。セッションの取得は最初に入力されているサンプルに含まれています。  

```py
# Import python packages
import streamlit as st
import pandas as pd

# We can also use Snowpark for our analyses!
from snowflake.snowpark.context import get_active_session
session = get_active_session()
```

![cell1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/ea38164f-ee93-fb14-c7ae-1c9152fd2d99.png)

### SQLセルからデータベースを参照

普通のSQLワークシートと変わりません。  

```sql
-- SQLセルからDatabaseを参照
select * from movies limit 100;
```

![cell2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/0bc44e16-ede3-b4f4-606b-67d2a2284464.png)

### Pythonセルからデータベースを参照

最初に取得したセッションを使用します。```table()```や```sql()```を使います。  
SnowflakeのライブラリであるSnowparkを使ったことがある人なら特に困らないと思います。  

```sql
# PythonセルからDatabaseを参照
# to_pandas()でPandas DataFrameに変換して扱う
ratings_df = session.table("RATINGS").to_pandas()
ratings_df = ratings_df.head()
# Pythonセルで定義された変数は後段の処理から変数名で参照できる
test_var = 100
# print()も使えるがマジックコマンド(変数名ベタ書き)で出力するのがNotebook的なので推奨
# マジックコマンドで出力すると後段の処理からセル名(cell3)でも参照できるようになる
ratings_df
```

![cell3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/d877f04f-8884-8cc7-d373-5ab4030bdb43.png)

### PythonセルからSQLセルを参照

セル名を使って参照します。  
SELECT文を実行したSQLセルは```SnowflakeQueryResult```という内部的に使用するオブジェクトなので、```to_pandas()```や```to_df()```でDataFrameに変換してあげます。  

```py
# PythonセルからSQLセルを参照
# セル名(cell2)で参照できる。cells.cell2としてもよい
movies_df = cell2.to_pandas()
movies_df
```

![cell4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/3d3832c2-6224-6a99-9952-ccaeb4ff7634.png)

### PythonセルからSQLセルを参照：Snowpark DataFrameに変換する

大きめのデータセットに対してDataFrameで色々な操作を行いたい場合は、Snowpark DataFrameを使う方が効率的かもしれません。  
```to_df()```でSnowpark DataFrameに変換できます。  

```python
# Snowpark DataFrameにも変換できる
snowpark_df = cell2.to_df()
snowpark_df
```

### SQLセルからSQLセルを参照

セル名を```{{ }}```で囲ってあげればOK。  

```sql
-- SQLセルからSQLセルを参照する
select * from {{cells.cell2}};
```

![cell5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/e6f70f6c-a8e7-2b22-762e-9ea87cc6103d.png)

### SQLセルからPythonセルで定義された変数を参照

こちらも変数名を```{{ }}```で囲ってあげればOK。

![cell6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/b807c1bb-9667-cac3-07b4-33e554f823d7.png)

```sql
-- SQLセルからPythonセルで定義された変数を参照する
select {{test_var}};
```

例えば以下のようなことができます。  

```sql
-- cell2はSQLセルの結果
-- test_varはPythonセルで定義された変数
-- これを組み合わせたSQL文を実行する
select * from {{cells.cell2}} where movieid = {{test_var}};
```

![cell7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/3799494b-7d8c-d78d-ae3a-b80e41ae1d21.png)

### DataFrameをテンポラリテーブルに書き出す

Snowpark DataFrameであれば簡単にテンポラリテーブルに書き出すことができます。  

```py
# Snowpark DataFrameにも変換できる
snowpark_df = cell2.to_df()
# テンポラリテーブルで書き出す
snowpark_df.write.mode("overwrite").save_as_table(table_name="cell8_tbl", table_type="temporary")
```

![cell8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/94b26e5b-e247-9bed-fd90-3259d96dd4b6.png)

### SQLセルからPythonセルで加工されたDataFrameを参照する

Pythonセルからテンポラリテーブルに書き出すようにすればシンプルに参照することができます。  
…がこれはもっといい方法が用意されてそうな気も。  

```sql
-- SQLセルからPythonセルのDataFrameを扱う方法がテンポラリテーブルを経由するしか思いつかなかった
-- もしかしたらもっといい方法が準備されているかも
select * from cell8_tbl;
```

![cell9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/6e1b503b-da18-ec7a-1962-44dfe588361b.png)

### Markdownセルを参照…できない

少し試した範囲ではPythonセルからMarkdownセルを参照することはできなさそうです。  
もしかしたら何か方法が用意されているかも。  

```python
# Markdownセルは参照できないらしい
print(cells.title)
```

![cell10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/33399ab6-5731-1b38-3817-308af4f356a0.png)

## お片付け

GUIでもノートブックを削除できますが、SQLで管理するための以下のようなコマンドが用意されています。

```sql
-- ノートブック一覧を取得する
SHOW NOTEBOOKS;

-- 削除する
DROP NOTEBOOK NOTEBOOK_TEST.PUBLIC."SAKATOKU 2024-05-26 23:14:35";
```

## 参考ドキュメント

以下のようなリソースが公開されています。  
まだまだ情報は少ないのですが、これからドキュメントやPuPrを試してみた記事がたくさん出てくると思うので、楽しみですね！  

[デモのソースコード](https://github.com/Snowflake-Labs/snowflake-demo-notebooks
)  

https://github.com/Snowflake-Labs/snowflake-demo-notebooks

[デモ動画のプレイリスト](https://www.youtube.com/playlist?list=PLavJpcg8cl1Efw8x_fBKmfA2AMwjUaeBI)  

https://www.youtube.com/playlist?list=PLavJpcg8cl1Efw8x_fBKmfA2AMwjUaeBI