Snowparkの環境設定（Jupyter Notebookを前提）

Snowparkにおける、Jupyter Notebookを前提にした、環境設定方法を記述致します。なお、あくまでも個人として記述しております。

## 前提
1. Python 3.8がインストール済みである
2. Snowflakeアカウントを保持していること
3. Mac OS(Version 13.2)を使用


## ディレクトリ構成
```
(任意のディレクトリ)
  ├── creds.json
  └── environments.yml
```

## 設定方法
#### 1. Minicondaをインストールする
　Miniconda/Anacondaのインストール方法に関する文献は多く存在するため、ここでは具体的な手順を割愛する。[ここから](https://repo.anaconda.com) ご自身の環境に適したインストラーを選択し、インストールして頂きたい。

#### 2. 環境作成
次に任意のディレクトリに移動後に、下記のCondaコマンドを実行すると、環境が作成される。

```terminal:terminal
conda env create -f environments.yml
```

ここで、environments.ymlの中身を解説する。
```yaml:environments.yml
name: snowpark
channels:
  - https://repo.anaconda.com/pkgs/snowflake
  - nodefaults
dependencies:
  - python=3.8
  - pip
  - pip:
    - snowflake-snowpark-python[pandas]==1.0.0
    - pandas==1.4.4
    - numpy==1.22.3
    - scikit-learn==1.1.1
    - jupyter
```

　1行目には環境の名前が記述されており、今回のケースで`snowpark`という名前としている。次に`channels`では、パッケージをインストールするチャンネルを指定している。このチャンネルは、UDFやStored Procedureなどで指定した際に呼びされるチャンネルと連携している。`dependencies`ではcondaコマンドでインストールするライブラリを記載し、サブディレクトリにある`pip:`に指定されているライブラリ群はpipでインストールされている。
　snowparkの正式な名前は`snowflake-snowpark-python`である。詳しくは[こちら](https://pypi.org/project/snowflake-snowpark-python/)をご参照して頂きたい。また、snowparkはpandasとの依存関係があるため、[pandas]と記述することで、pandasのバージョンに合わせたライブラリを自動的にインストールすることができる。他にpandas/numpy/scikit-learnなど必要なライブラリをインストールしているが、これは必要に応じて、変更/削除して頂きたい。

#### 3. 環境を切り替える

　非常にシンプルであるが、以下のコマンドで環境の切り替えを行う。

```terminal:terminal
conda activate snowpark
```

　なお、余談であるが、既存環境情報を出力する方法は、以下の通りである。

```terminal:terminal
conda env export > hogehoge.yml
```

#### 4. Jupyter Notebookを起動
　ターミナル上で下記コマンドを実行し、Jupyter Notebookを実行する
```terminal:terminal
jupyter notebook
```

　Jupyter Notebookのウェブ画面の右上にあるNewから`「Python3(ipykernel)」`を指定し、ノートブックを起動する。

#### 5. Snowflakeへの接続

　まず、Snowflakeへの接続情報を保持している`creds.json`というConfigファイルを用意する。詳細は下記の通りになっている。
```json:creds.json
{
    "account": "<Your Account Loacator>", 
    "user": "<Your User>", 
    "role": "<Your Role>", 
    "password": "<Your Password>"
}
```
　
　上記には、皆様のお持ちの必要な情報を記載して欲しい。また、account欄では二つのURLが有効である。なお、どちらのパターンでも **.snowflakecomputing.com**を含んではならない。

- アカウントロケーターを使用する場合：xyz1234.ap-northeast-1.aws
- リージョンレスURLを使用する場合：sforg-xyz123

　Snowflakeにおけるアカウント識別子を理解したい場合、[こちら](https://docs.snowflake.com/ja/user-guide/admin-account-identifier.html#format-1-preferred-account-name-in-your-organization)を参考にしてほしい。また、Private Linkを使用している場合はURLが異なるので注意する必要がある。

※ 以下のMFA時の登録方法について、2/7に追加しました

　MFAを設定している場合、時々、プッシュ通知が飛んで来なかったりする場合がある。その場合には、Duo Mobileが発行する6桁のパスコードを明示的に入力することでSnowflakeの接続を確立することができる。具体的には、コネクションのパラメータにPasscodeというキーと、それにDuo Mobileが発行するパスコードを入力することで接続が可能となる。なお、SMS認証では`「EXT_AUTHN_DUO_PUSH_DISABLED
Duo Push is not enabled for your MFA. Provide a passcode as part of the connection string.」`ではというエラーが発生するように見える。PythonとMFAを設定する上での詳細について、[こちら](https://docs.snowflake.com/ja/user-guide/security-mfa.html#using-mfa-with-python)を確認して欲しい。具体的なコードは、以下の通りである。

```json:creds.json
{
    "account": "<Your Account Loacator>", 
    "user": "<Your User>", 
    "role": "<Your Role>", 
    "password": "<Your Password>",
    "passcode": "<Duo Mobile's Passcode>" 
}
```


　その後にJupyter Notebookに戻り、任意のセル内に以下のコードを貼り付ける。これは、クレデンシャル情報を読み込み、Snowflakeへ接続を実行している。
```python:Jupyter Notebook
import json
from snowflake.snowpark.session import Session

with open('creds.json') as f:
    data = json.load(f)
    USERNAME = data['user']
    PASSWORD = data['password']
    ACCOUNT = data['account']
    ROLE = data['role']

CONNECTION_PARAMETERS = {
   "account": ACCOUNT,
   "user": USERNAME,
   "password": PASSWORD,
   "role": ROLE
}
session = Session.builder.configs(CONNECTION_PARAMETERS).create()
```

　Snowflakeへの接続が完了しているか、下記のコマンドで確認する。現在、指定されているロールやお使いのSnowparkのバージョンの情報が返ってくれば、Snowflakeへの接続が完了している。
```python:Jupyter Notebook
from snowflake.snowpark import version as v
snowpark_version = v.VERSION
print('Role                        : {}'.format(session.get_current_role()))
print('Snowpark for Python version : {}.{}.{}'.format(snowpark_version[0],snowpark_version[1],snowpark_version[2]))
```

#### 6. 任意のコマンドを実行する

　上記の接続情報には、対象のデータベース/スキーマや使用するウェアハウスの情報がなかったため、そちらを用意し、指定する。
```python:Jupyter Notebook
session.sql("CREATE DATABASE IF NOT EXISTS SNOWPARK;").collect()
session.use_database('SNOWPARK')
session.sql('CREATE SCHEMA IF NOT EXISTS SNOWPARK').collect()
session.use_schema('SNOWPARK')
session.sql('CREATE WAREHOUSE IF NOT EXISTS COMPUTE_WH')
session.use_warehouse('COMPUTE_WH')
print('Database                    : {}'.format(session.get_current_database()))
print('Schema                      : {}'.format(session.get_current_schema()))
print('WAREHOUSE                   : {}'.format(session.get_current_warehouse()))
```

　`sql`メソッドを使い、各種オブジェクトを作成し、`use`関数で指定している。最後に`get`関数で現在のポイントされているオブジェクトの情報を取得する。これで、指定した情報が標準出力上に表示されれば問題なく、仮に`None`という値が表示される場合は、どこかが間違っているため、再度、確認する必要がある。

　最後に、以下の`sql`メソッドを使用し、任意のクエリを発行する。
```python:Jupyter Notebook
session.sql("select 1+2").show()
```

　これで`3`という値が返ってくれば、問題なく、snowparkをお使いになることができる。

#### 7. 環境の削除
　以下のコマンドを使用すると、作成した環境を削除することができる。

```terminal:terminal
conda remove -n snowpark --all
```

## 参考情報
最後にsnowparkとcondaコマンドの参考情報を載せておく。
1. [Snowpark API](https://docs.snowflake.com/ja/developer-guide/snowpark/reference/python/index.html)
2. [Condaコマンドに関するチートシート](https://docs.conda.io/projects/conda/en/4.6.0/_downloads/52a95608c49671267e40c689e0bc00ca/conda-cheatsheet.pdf)