遅いPythonの処理 は Julia にやらせよう

# ■ 投稿理由（動機）
 - Python から Julia を呼び出す処理は、Julia のファイル名を指定して実行させるものがほとんどで、**Python から Julia へ引数を渡して処理させる方法が見当たらなかった**ので投稿しました。
 - なお、**Pandas の DataFrame も引数で渡せるので、適用範囲はかなり広がる**と思います。
 - **Python の欠点である演算速度の遅さを、Julia でカバーできる**と思い記事にしました。   
  下のグラフは私の非力ＰＣで処理した結果で、**Julia圧勝**です。　Python：327秒　Julia:3.3秒
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234391/59242a7d-fa24-2daf-3bcb-863191cf3bec.png)

# ■ ライブラリのインストール
 ◇ Python側：[**PyJulia**](https://pypi.org/project/julia/) のインストール

```
pip install julia # Juliaとの連携
```

 ◇ Julia側：[**PyCall**](https://github.com/JuliaPy/PyCall.jl) と [**JSON**](https://github.com/JuliaIO/JSON.jl) のインストール

```
using Pkg; Pkg.add("PyCall") # Pythonとの連携
using Pkg; Pkg.add("JSON") # JSONデータのパース（構文解析）
```

# ■ コード
## ◇ **call_julia.py** 【Python】

```python:call_julia.py
from julia import Julia # !pip install julia
Julia(compiled_modules = False) # PyJuliaの設定変更
from julia import Main as call_julia
import pandas as pd

# "call_julia.jl"を取り込み、call_juliaインスタンス を生成
call_julia.include("call_julia.jl")

# データ型変換用のデコレータ　DataFrame(Python) <-> json(Julia)
def df2json(func):
    def wrapper(in_df):
        in_json = in_df.to_json(orient='records') # dataframe -> json
        out_json = func(in_json)                  # json -> func -> json
        out_df = pd.read_json(out_json)           # json -> dataframe 
        return out_df
    return wrapper

# 引数なし
def hello_world() -> str:
    return call_julia.hello_world()

# 引数あり
def fib(n: int) -> int:
    return call_julia.fib(n)

# 引数がデータフレーム  dataframe -> json 渡し
@df2json
def calc_df(in_json: str) -> str:
    return call_julia.calc_df(in_json)
```

## ◇ **call_julia.jl** 【Julia】

```julia:call_julia.jl
using JSON # using Pkg; Pkg.add("JSON")

# 引数なし
function hello_world() # ハローワールド
    "Hello World"
end

# 引数あり
function fib(n) # フェボナッチ数列（再帰ヴァージョン）
    if n == 0
        return 0 
    elseif n < 3
        return 1
    end
    fib(n-1) + fib(n-2)
end

# 引数がデータフレーム  dataframe -> json 渡し
function calc_df(in_json)
    in_dic = JSON.parse(in_json)
    
    for dic in in_dic
        dic["join"] = string(dic["No"]) * dic["item"]
    end    
    
    JSON.json(in_dic)
end
```

# ■ 使い方（実行画面）
 - **実行ファイル（.ipynb, .py）**は、**call_julia.py**，**call_julia.jl** と**同じフォルダ**に置いて下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234391/2d09b330-e1eb-5806-ba9d-3556a431396d.png)

# ■ 特記事項（工夫した点）
 - [**call_julia.py**](https://qiita.com/malo21st/items/3b0f96713cb433202725#-call_juliapy-python) の引数をデータフレームで渡す処理は、DataFrame を JSON に変換して Julia側 に渡しています。そして、Julia側 の処理結果も JSON から DataFrame へ変換して返しています。これら一連の処理は、**デコレータ（df2json）**を使って実装しました。

# ■ Python と Julia が動く環境の構築方法
 - 私の場合は、**dockerhub から [jupyter/datascience-notebook](https://hub.docker.com/r/jupyter/datascience-notebook) という docker image をプルして構築しました**。参考までに docker run スクリプトはこんな感じです。

```
docker run -v D:\DS\temp\work:/home/jovyan/work -p 9998:8888 --name prj-env jupyter/datascience-notebook
```

 - なお、**D:\DS\temp\work と 9998 と prj-env の箇所は、任意に決めていただいて結構です**。   
この環境では、Python と Julia だけでなく Ｒ も JupyterNotebook上で動きます。

# （補足）DataFrames.jl との DataFrame連係

https://qiita.com/malo21st/items/8f56a171dd7fa60774e6