Jupyter Lab で Julia を動かす

これまでの Python での仕事をすこしずつ Julia に移行する試みのひとつとして、Jupyter Lab 上で Julia を動かす環境を構築しました。Jupyter を駆動する Python の仮想環境と Jupyter の環境の関連などで混乱したので、整理してまとめます。

単に Jupyter 上で Julia が動くだけでなく、既存の Python 資産を素直な形で利用したかったので、

- Python の仮想環境を活用すること、
- Python の仮想環境にインストールした Jupyter を利用すること、
- Julia から仮想環境上の Python パッケージを利用できること

を目標とし、これらを達成しました。

設定した環境は macOS Monterey (v12.3), Apple M1 の ARM 環境です。環境依存性はあまりないので、macOS の Rosetta 環境や各種 Linux でも同様でしょう。Windows では確認していません。

[動作確認](#実行の確認)は以下を実施しました。

- PlotlyJS を用いた可視化
    Julia にネイティブ対応したパッケージを用いた検証実験。

- Plotly Express を用いた可視化
    外部関数呼び出しインタフェイスの PyCall.jl を介して Python 用のパッケージを呼び出した検証実験。

ごちゃごちゃした説明は抜きに、コードだけ見たい方は[私の GIST](https://gist.github.com/wakita/87b39e8d463f3d279c21c3e15a1cd9c7)を見るとよいかもしれません。Mac 上の二種類の Python 環境を用いて、自動的に環境構築するコードになっています。

# 構築方法とその概要

1. [**Jupyter Lab 環境の構築**](https://qiita.com/kwakita/items/fdac0deb5db48efca48c#jupyter-lab-環境の構築)

    Jupyter を駆動するための `venv` 仮想環境を作成し、Jupyter Lab とその拡張機能をインストールします。

1. [**環境変数群の設定**](https://qiita.com/kwakita/items/fdac0deb5db48efca48c#環境変数群の設定)

    [IJulia](https://github.com/JuliaLang/IJulia.jl) は Julia 用の Jupyter バックエンドです。このパッケージが Jupyter と Julia のなかだちをしてくれます。Jupyter の在処を Julia 側に伝えるために環境変数 (`JUPYTER`) を設定します。

    Julia から Python の資産を利用する場合、もうひとつの大切な環境変数があります。Python インタプリタを指し示す`PYTHON`変数は Julia が [PyCall](https://github.com/JuliaPy/PyCall.jl) パッケージを介して Python 資産を探すときのとっかかりを与えます。

1. [**Juliaの環境構築**](https://qiita.com/kwakita/items/fdac0deb5db48efca48c#juliaの環境構築)

    Julia を起動し、環境を設定・構成します。これで実行環境の準備が整います。

## Jupyter Lab 環境の構築

まず、Python の仮想環境を新規構築します。以下の例では `$HOME/.venvs/plotly` に構築していますが、このディレクトリはどこに作成しても構いません。

以下の作業は Apple M1 を搭載した Mac の ARM 環境で実施しましたが、環境依存性は少ないです。Apple M1 Mac の Rossetta 環境や Linux 環境でもそのまま構築できると思います。Anaconda 版の Python でやっても問題はないでしょう。Windows については未確認です。

Jupyter Lab を駆動する Python は Jupyter Lab が対応する十分に新しい Python を利用して下さい。以下では Mac にもともと搭載されている `/usr/bin/python3` を用います。Homebrew 版 (`/opt/homebrew/bin/python3.9`) でも同様の手順で動作することを確認しています。

では、仮想環境を作成し、利用しましょう。

~~~ interact1.sh
$ venv=$HOME/.venvs/plotly       # 便利のよいディレクトリを指定して下さい
$ mkdir -p $venv
$ /usr/bin/python3 -m venv $venv
$ source $venv/bin/activate
~~~

次に仮想環境に必要なソフトウェアをインストールします。

~~~ interact2.sh
$ $venv/bin/pip install --upgrade pip jupyterlab
$ $venv/bin/jupyter labextension install jupyterlab-plotly
~~~

`pip` を使って Jupyter Lab をインストールします。

第二行目は Julia 用の強力なデータ可視化パッケージの PlotlyJS と Jupyter Lab を連携させるための設定です。データ可視化に興味がない人は必要ありません。

二番目のコマンドで `jupyter` のパスを明示している点は大切です。第一行目でインストールしたばかりの Jupyter は PATH に含まれているはずですが、シェルのキャッシュに載っていないようです。このため、単に `jupyter labextension ...` と起動すると、どこか別の環境の Jupyter 環境へのインストールが始まります。また、Jupyter が起動したときに、あるべき拡張機能がインストールされていないために動作がおかしくなります。

※ このほかに利用したい Python パッケージがあったら、ここでインストールしておいて下さい。

## 環境変数群の設定

**IJuliaとJupyterの連携 (`JUPYTER`)**: IJulia から `IJulia.jupyterlab()` 関数を介して Jupyter Lab を開始するときに起動すべき Jupyter コマンドの在処を指定します。すでに仮想機械に Jupyter をインストールしてあるので、そのパスを与えます。

~~~ interact3.sh
$ export JUPYTER=$venv/bin/jupyter
~~~

※ この変数の設定を変更したときは、その変更内容を IJulia パッケージに通知するために、Julia パッケージの再構成 (`Pkg.build("IJulia")`)が求められるようです。環境変数設定を誤ったときに、環境変数を変更しただけでは設定がうまく IJulia に通知されないようです。

**JuliaとPython仮想機械の連携 (`PYTHON`)**: Julia から PyCall パッケージを介して Python 資産にアクセスするときに、探索すべき仮想機械の在処を指し示すために、仮想機械の Python インタプリタの場所を設定します。

~~~ interact4.sh
$ export PYTHON=$venv/bin/python3
~~~

この変数の設定を変更したときは、その変更内容を PyCall パッケージに通知するために、PyCall パッケージの再構成(`Pkg.build("PyCall")`)が求められるようです。

## Juliaの環境構築

Julia の外部環境は設定がすみました。

いよいよ Julia を起動し、残りの環境設定を施します。

- `IJulia`: Jupyter Lab とのなかだちをするパッケージなのです。Jupyter を利用する上で必須のパッケージです。
- `PlotlyJS`: 可視化のために使うパッケージなので、可視化に興味がなければ不要です。
- `PyCall`: Julia から Python のパッケージやプログラムをアクセスするためのパッケージです。今回の例では、Python版の Plotly を利用するときに用います。

これらのパッケージをインストールするために `Pkg` パッケージを利用します。Julia REPL のパッケージモードを利用しても構いません。

必要ないかもしれませんが、念のためここでインストールするすべてのパッケージを再構成 (`Pkg.build(pkg)`) しています。

~~~ interact5.sh
$ mkdir sample
$ cd sample
$ julia --project=.
julia> using Pkg
julia> for pkg in ["IJulia", "PyCall", "PlotlyJS"]
           Pkg.add(pkg); Pkg.build(pkg)
       end
~~~

お疲れさま。これで準備完了です。

# Jupyter の起動

あとは、IJulia パッケージの `jupyterlab()` 関数を呼べば Jupyter Lab が起動するはずです。

~~~ interact6.julia
julia> using IJulia
julia> jupyterlab()
~~~

環境変数 (`JUPYTER` と `PYTHON`) が環境構築のときだけに必要なのか、IJupyter や PyCall を呼ぶたびに必要なのかは確認していないのですが、不安なので私は毎回、設定しています。でも、面倒な手作業を避けるために小さなシェルスクリプト(`lab`)を用意しています。

~~~ lab
#!/bin/bash

#=
venv=$HOME/.venvs/plotly
export JUPYTER="$venv/bin/jupyter"
export PYTHON="$venv/bin/python3"

# Python 仮想環境の起動
source $venv/bin/activate

# Julia の起動
exec julia --project=. --startup-file=no -e 'include(popfirst!(ARGS))' "$0" "$@"

=#

using IJulia
jupyterlab()
~~~

# 実行の確認

データ可視化パッケージの PlotlyJS を利用した以下のコードは20個の乱数値を折れ線グラフに描画します。

~~~ sample1.jl
using PlotlyJS

plot(rand(20))
~~~

環境がうまく構築できていれば、（結果は毎回異なりますが）以下のような出力が得られるでしょう。

![簡単な可視化例](https://i.gyazo.com/9260d8d9aba0fcc622338c91db2ae1ee.png)

次は PyCall を使って、Python の資産を利用する例です。

~~~ sample2.jl
import PyCall
px = PyCall.pyimport("plotly.express")

gapminder = px.data.gapminder()

animation_fig = px.scatter(gapminder,
    x="gdpPercap", y="lifeExp",
    log_x=true,
    hover_name="country",
    size="pop", size_max=40,
    color="continent",
    facet_col="continent",
    width=800,
    animation_frame="year")

animation_fig.update_xaxes(tickfont=Dict("size" => 8))
animation_fig
~~~

これを実行すると、以下のような出力が得られます。下部のボタンでアニメーションを再生したり、スライダー操作で年ごとのデータを分析することができます。

![PlotlyJSで実装されたGapMinderの可視化](https://i.gyazo.com/b7800ef96df59f0f5a557ac92a6a6df1.png)

このコードでは、Python 資産にアクセスするための PyCall を利用しています。`PyCall.pyimport("plotly.express")` によって、Python 用のデータ可視化パッケージ Plotly の機能にアクセスしています。

この Python パッケージには「[FACTFULLNESS](https://www.amazon.co.jp/dp/B07LG7TG5N/)」の著作でも知られるハンスロスリングの GapMinder データセットが含まれています。これを読み込み、Plotly Express によって散布図を生成し、その `animation_frame` 属性によってアニメーションを付加しています。

[Plotly Express](https://plotly.com/python/plotly-express/) の使い方は、@driller, 小川、古木著の「[Pythonインタラクティブ・データビジュアライゼーション入門](https://www.amazon.co.jp/gp/product/B09JFHZ7XH/)」のコードのほぼ丸パクリです。