【Dr.Sum】インメモリビューのデータを利用した相関係数出力

# 目次
- はじめに
- 前提条件
- データの用意
- エミュレータを使用したデバッグ
- 実環境での実行
- おわりに

# はじめに
データ分析において現状蓄積されているデータからざっくりと論理的にデータ同士の関連性を導きたい。
Dr.Sum Ver.5.6からPyhonスクリプトを実行する機能が追加されたようなので[公式マニュアル](https://cs.wingarc.com/manual/drsum/5.6/ja/UUID-eada69d6-5fd7-1a57-747f-33318c15cae8.html)を参考に列項目に対して総当り的に相関係数を出力するロジックを実装してみる。
少しややこしいがDr.SumのPython連携ではインストール時にクライアント（デバッグ）用、サーバー（実環境）用の[実行環境](https://cs.wingarc.com/manual/drsum/5.6/ja/UUID-77ac2bf3-8cdf-f86a-c15b-2ad23c9ac765.html)がインストール時に提供されており、
そちらを利用するため本記事では適時Python実行環境を切り替えながら実装を行う。

# 前提条件
- Dr.SumはLocalhost:6001で実行
- Dr.Sumインストールフォルダ：[C:\DrSum56]
- クライアント用Python実行環境：[C:\DrSum56\AdminTools\udtf-python\python\python.exe]
- サーバー用Python実行環境：[C:\DrSum56\IMServer\launch\python\env\python\python.exe]
- Python連携ルート設定は[C:\DrSum56\Server\samples\udtf-py]
- 今回はscikit-learnに含まれているサンプルデータセットの「ボストンの住宅価格データ」を使用

# データの用意
今後重回帰分析等の検証も行いたいため、scikit-learnをインストールし「ボストンの住宅価格データ」をCSVエクスポートしてDr.Sumのテーブルにインポートを行う。
こちらはあくまでサンプルデータを出力するためのプログラムのため任意のPython環境で実行して問題ありません。

```console
pip install pandas
```

```console
pip install sklearn
```

```python:sample_output.py
import pandas as pd
from sklearn.datasets import load_boston


if __name__ == "__main__":
    # ボストンの住宅価格データ（サンプルデータセット）の読み込み
    dataset = load_boston()
    # データフレームに変換
    df = pd.DataFrame(dataset.data, columns=dataset.feature_names)
    # # index除外でCSV出力
    df.to_csv("boston.csv", index=False)
```

インメモリ化されたビューのみが参照可能なようなので出力したCSVを以下の手順で処理する。

1. Dr.Sumのテーブルとしてインポート
1. ビューを作成（上記テーブルを参照）→本記事ではビュー名：boston_viewとする。
1. ビューをインメモリ化
![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1571692/b7f6a766-f6be-41e0-b426-2d307e5f45d8.png)


# エミュレータを使用したデバッグ
クライアント側で実行するための相関係数を出力するロジックを実装する。
以下の実装が必要
- エミュレータ実行部：今回は"__main__"で処理
- ロジック部：入力データを計算し出力データとして返す（今回は相関係数算出部）
- 出力データスキーマ定義部：エミュレータが出力

#### 実行ファイルの配置
[【Python連携ルート】/script]へ実行ファイルを作成

#### 相関係数取得ロジック実装
「出力データスキーマメソッド」は実行後エミュレータが出力してくれるため、この段階ではメソッド名のみ定義しておく。
入力データのスキーマが変わっても以下のコードを実行して「出力データスキーマメソッド」を出力し直して書き換えれば再利用可能です。
JDBCでのDr.Sumへのアクセス方法は[こちら](https://qiita.com/genovesemosuki/private/6c1e880aa96c4b6e99d8)を参照。
相関係数の算出はPandasのcorrメソッドで実行。
またDr.Sumの仕様に合わせてロジック部分の引数、戻り値は共にPandasのDataFrameで扱う。
ローカルPython環境にライブラリをインストールしているかに関わらず別途Dr.Sum
のクライアント用Python実行環境にもライブラリのインストールが必要。

```
C:\DrSum56\AdminTools\udtf-python\python\python.exe -m pip install pandas
C:\DrSum56\AdminTools\udtf-python\python\python.exe -m pip install jaydebeapi
```

```python:correlation.py
import pandas as pd
import jaydebeapi
import os


def get_corr(context, input):
    # 相関係数の算出
    corr = input.corr()
    return corr


if __name__ == "__main__":
    cwd = os.path.dirname(__file__)
    import dspy.emulator
    # Dr.SumのビューをJDBCで参照
    conn = jaydebeapi.connect("jp.co.dw_sapporo.JDBC.JDBCDriver",
                              "jdbc:dwods:localhost:6001:testDB",
                              ["User", "Pass"],
                              "C:\DrSum56\DevKit\java\jdbc\dwodsjd4.jar")
    curs = conn.cursor()
    sql = "select * from boston_view"
    sql_query = pd.read_sql_query(sql, conn)

    print("----- inputDr.Sum -----")
    # inputとなるデータのスキーマ解釈を出力
    dspy.emulator.show_ds_schema(sql_query)

    builder = dspy.emulator.EmulatorBuilder(
        sql_query,
        # show_ds_schema()の型解釈結果が認識と違う場合指定（省略可）
        # adjust_input_type={'MEDV_PRICE': 'NUMERIC', 'CRIM': 'NUMERIC', 'ZN': 'NUMERIC', 'INDUS': 'NUMERIC', 'CHAS': 'NUMERIC', 'NOX': 'NUMERIC',
        #                    'RM': 'NUMERIC', 'AGE': 'NUMERIC', 'DIS': 'NUMERIC', 'RAD': 'NUMERIC', 'TAX': 'NUMERIC', 'PTRATIO': 'NUMERIC', 'B': 'NUMERIC', 'LSTAT': 'NUMERIC'},
        # エミュレータにおけるPY_SCRIPT_ROOT（省略時はカレントフォルダー）
        py_script_root=os.path.join(cwd, "../script"),
        # エミュレータにおけるPY_DATA_ROOT（省略時はカレントフォルダー）
        py_data_root=os.path.join(cwd, "../data")
    )
    emulator = builder.buildSerial(
        # 対象のPythonファイル名（当ファイル）
        py_file_path="PY_SCRIPT_ROOT/correlation.py",
        # 対象の実行ロジックメソッド
        func_name="get_corr",
        # 出力データスキーマメソッド名
        schema_func_name="get_corr_schema",
    )
    curs.close()
    conn.close()
    # create_schema_mode=Trueで出力データスキーマメソッドを出力
    result = emulator.execute(create_schema_mode=True)
    print("----- OUTPUT -----")
    print(result)
```

実行(※エミュレータを使用するためクライアント用Python実行環境を使用する)

```
cd C:\DrSum56\Server\samples\udtf-py\script
C:\DrSum56\Server\samples\udtf-py\script>C:\DrSum56\AdminTools\udtf-python\python\python.exe correlation.py
```

出力結果

```console
----- inputDr.Sum -----
{'MEDV_PRICE': 'NUMERIC', 'CRIM': 'NUMERIC', 'ZN': 'NUMERIC', 'INDUS': 'NUMERIC', 'CHAS': 'NUMERIC', 'NOX': 'NUMERIC', 'RM': 'NUMERIC', 'AGE': 'NUMERIC', 'DIS': 'NUMERIC', 'RAD': 'NUMERIC', 'TAX': 'NUMERIC', 'PTRATIO': 'NUMERIC', 'B': 'NUMERIC', 'LSTAT': 'NUMERIC'}


以下のスタブ関数をC:\DrSum56\Server\samples\udtf-py\script\../script\correlation.pyにコピーしてください。

--------------------------------------------------------------------------------
def get_corr_schema(input_schema):
    return ['MEDV_PRICE NUMERIC', 'CRIM NUMERIC', 'ZN NUMERIC', 'INDUS NUMERIC', 'CHAS NUMERIC', 'NOX NUMERIC', 'RM NUMERIC', 'AGE NUMERIC', 'DIS NUMERIC', 'RAD NUMERIC', 'TAX NUMERIC', 'PTRATIO NUMERIC', 'B NUMERIC', 'LSTAT NUMERIC']
--------------------------------------------------------------------------------

----- OUTPUT -----
    MEDV_PRICE      CRIM        ZN     INDUS      CHAS       NOX        RM       AGE       DIS       RAD       TAX   PTRATIO         B     LSTAT
0     1.000000 -0.388305  0.360445 -0.483725  0.175260 -0.427321  0.695360 -0.376955  0.249929 -0.381626 -0.468536 -0.507787  0.333461 -0.737663
1    -0.388305  1.000000 -0.200469  0.406583 -0.055892  0.420972 -0.219247  0.352734 -0.379670  0.625505  0.582764  0.289946 -0.385064  0.455621
2     0.360445 -0.200469  1.000000 -0.533828 -0.042697 -0.516604  0.311991 -0.569537  0.664408 -0.311948 -0.314563 -0.391679  0.175520 -0.412995
3    -0.483725  0.406583 -0.533828  1.000000  0.062938  0.763651 -0.391676  0.644779 -0.708027  0.595129  0.720760  0.383248 -0.356977  0.603800
4     0.175260 -0.055892 -0.042697  0.062938  1.000000  0.091203  0.091251  0.086518 -0.099176 -0.007368 -0.035587 -0.121515  0.048788 -0.053929
5    -0.427321  0.420972 -0.516604  0.763651  0.091203  1.000000 -0.302188  0.731470 -0.769230  0.611441  0.668023  0.188933 -0.380051  0.590879
6     0.695360 -0.219247  0.311991 -0.391676  0.091251 -0.302188  1.000000 -0.240265  0.205246 -0.209847 -0.292048 -0.355501  0.128069 -0.613808
7    -0.376955  0.352734 -0.569537  0.644779  0.086518  0.731470 -0.240265  1.000000 -0.747881  0.456022  0.506456  0.261515 -0.273534  0.602339
8     0.249929 -0.379670  0.664408 -0.708027 -0.099176 -0.769230  0.205246 -0.747881  1.000000 -0.494588 -0.534432 -0.232471  0.291512 -0.496996
9    -0.381626  0.625505 -0.311948  0.595129 -0.007368  0.611441 -0.209847  0.456022 -0.494588  1.000000  0.910228  0.464741 -0.444413  0.488676
10   -0.468536  0.582764 -0.314563  0.720760 -0.035587  0.668023 -0.292048  0.506456 -0.534432  0.910228  1.000000  0.460853 -0.441808  0.543993
11   -0.507787  0.289946 -0.391679  0.383248 -0.121515  0.188933 -0.355501  0.261515 -0.232471  0.464741  0.460853  1.000000 -0.177383  0.374044
12    0.333461 -0.385064  0.175520 -0.356977  0.048788 -0.380051  0.128069 -0.273534  0.291512 -0.444413 -0.441808 -0.177383  1.000000 -0.366087
13   -0.737663  0.455621 -0.412995  0.603800 -0.053929  0.590879 -0.613808  0.602339 -0.496996  0.488676  0.543993  0.374044 -0.366087  1.000000
```

OUTPUTを見るとロジック部はどうやらうまくいってそうである。
> 以下のスタブ関数をC:\DrSum56\Server\samples\udtf-py\script\../script\correlation.pyにコピーしてください。

と「出力データスキーマのメソッド」が出力されたのでソースに追加して、
ついでにこの出力はメソッド追加後不要のためcreate_schema_mode=Falseにして標準出力しないようにする。
ソースの最終形は下記。

```python:correlation.py
import pandas as pd
import jaydebeapi
import os


def get_corr_schema(input_schema):
    # 出力データスキーマ
    return ['MEDV_PRICE NUMERIC', 'CRIM NUMERIC', 'ZN NUMERIC', 'INDUS NUMERIC', 'CHAS NUMERIC', 'NOX NUMERIC', 'RM NUMERIC', 'AGE NUMERIC', 'DIS NUMERIC', 'RAD NUMERIC', 'TAX NUMERIC', 'PTRATIO NUMERIC', 'B NUMERIC', 'LSTAT NUMERIC']


def get_corr(context, input):
    # 相関係数の算出
    corr = input.corr()
    return corr


if __name__ == "__main__":
    cwd = os.path.dirname(__file__)
    import dspy.emulator
    # Dr.SumのビューをJDBCで参照
    conn = jaydebeapi.connect("jp.co.dw_sapporo.JDBC.JDBCDriver",
                              "jdbc:dwods:localhost:6001:testDB",
                              ["User", "Pass"],
                              "C:\DrSum56\DevKit\java\jdbc\dwodsjd4.jar")
    curs = conn.cursor()
    sql = "select * from boston_view"
    sql_query = pd.read_sql_query(sql, conn)

    print("----- inputDr.Sum -----")
    # inputとなるデータのスキーマ解釈を出力
    dspy.emulator.show_ds_schema(sql_query)

    builder = dspy.emulator.EmulatorBuilder(
        sql_query,
        # show_ds_schema()の型解釈結果が認識と違う場合指定（省略可）
        # adjust_input_type={'MEDV_PRICE': 'NUMERIC', 'CRIM': 'NUMERIC', 'ZN': 'NUMERIC', 'INDUS': 'NUMERIC', 'CHAS': 'NUMERIC', 'NOX': 'NUMERIC',
        #                    'RM': 'NUMERIC', 'AGE': 'NUMERIC', 'DIS': 'NUMERIC', 'RAD': 'NUMERIC', 'TAX': 'NUMERIC', 'PTRATIO': 'NUMERIC', 'B': 'NUMERIC', 'LSTAT': 'NUMERIC'},
        # エミュレータにおけるPY_SCRIPT_ROOT（省略時はカレントフォルダー）
        py_script_root=os.path.join(cwd, "../script"),
        # エミュレータにおけるPY_DATA_ROOT（省略時はカレントフォルダー）
        py_data_root=os.path.join(cwd, "../data")
    )
    emulator = builder.buildSerial(
        # 対象のPythonファイル名（当ファイル）
        py_file_path="PY_SCRIPT_ROOT/correlation.py",
        # 対象の実行ロジックメソッド
        func_name="get_corr",
        # 出力データスキーマメソッド名
        schema_func_name="get_corr_schema",
    )
    curs.close()
    conn.close()
    # create_schema_mode=Trueで出力データスキーマメソッドを出力
    result = emulator.execute(create_schema_mode=False)
    print("----- OUTPUT -----")
    print(result)
```

# 実環境での実行
例によりサーバー側のPython実行環境にもライブラリのインストールが必要なため実行
（【if __name__ == "__main__":】の中は実環境での実行時は通らないため、今回の場合JayDeBeApiに関してはインポート文のコメントアウトでも可）

```
C:\DrSum56\IMServer\launch\python\env\python\python.exe -m pip install pandas
C:\DrSum56\IMServer\launch\python\env\python\python.exe -m pip install jaydebeapi
```

Dr.SumのSQL Executorで以下を実行

```sql
SELECT
*
FROM
udtf::serial_py( boston_view, py_file_path='PY_SCRIPT_ROOT/correlation.py' , func_name='get_corr' , schema_func_name='get_corr_schema' ) T
```

出力結果
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1571692/6398ff91-19be-a394-053f-390e0841a986.png)

相関行列の結果から、MEDV_PRICEに対する相関係数が大きいのはRM、LSTATだということが分かる。

# おわりに
相関係数は無事出力できたので、次回はこの結果を受けて他の分析手法も試していきたい。

### 参考記事

- Dr.Sum関連マニュアル
    - [Python連携概要](https://cs.wingarc.com/manual/drsum/5.6/ja/UUID-eada69d6-5fd7-1a57-747f-33318c15cae8.html)
    - [Python実行環境](https://cs.wingarc.com/manual/drsum/5.6/ja/UUID-77ac2bf3-8cdf-f86a-c15b-2ad23c9ac765.html)
- Python実装関連
    - [Dr.Sum JDBC接続](https://qiita.com/genovesemosuki/private/6c1e880aa96c4b6e99d8)
    - [【初心者でも簡単】Pythonのscikit-learnで重回帰分析](https://navaclass.com/python-multiple-regression/)