なにはともあれ,最初に using PyCall 追記あり(2021/09/13)
using PyCall
Python のパッケージの import は以下のようにする
math = pyimport("math") # import math as math に相当
パッケージ中の関数は Python で記述するそのまま
math.sin(math.pi / 4) # 0.7071067811865475 # Python の math パッケージの sin と π
sin(pi / 4) # 0.7071067811865475 Julia の sin 関数と π
np = pyimport("numpy") # import numpy as np に相当
np.arcsin(0.5)
arcsin(0.5) # UndefVarError: arcsin not defined
# Julia には arcsin という関数はないのでエラーになる
asin(0.5) # 0.5235987755982989 # Julia では asin という名前
math.asin(0.5) # 0.5235987755982988 # Python の math パッケージでは asin という名前
matplotlib.pyplot での描画
plt = pyimport("matplotlib.pyplot")
x = range(0;stop=2*pi,length=1000); # Julia で定義
y = sin.(3*x + 4*cos.(2*x)); # Julia で定義
plt.plot(x, y, color="red", linewidth=2.0, linestyle="--")
plt.show()
上のようにして PyPlot するのではなく,Julia で以下のようにすれば同じようにプロットできる(PyCall は明示的には使わない)。
using PyPlot
x = range(0;stop=2*pi,length=1000); # Julia で定義
y = sin.(3*x + 4*cos.(2*x)); # Julia で定義
plot(x, y, color="red", linewidth=2.0, linestyle="--")
あるライブラリをインポートしたときにエラーになるときの対処法
so = pyimport("scipy.optimize")
これを実行したとき,
PyError (PyImport_ImportModule
The Python package scipy.optimize could not be imported by pyimport.
と言われた場合は,scipy.optimize がないということである。
いつも使っている Python には scipy.optimize はあるのに?という場合は,エラーメッセージに示されているサジェスチョンに従う必要がある。
Julia が想定する Python は Conda.jl パッケージ でインストールされる最小限の Python(minimal Python distribution)である。そこには scipy.optimize はないということである。
いくつかある対処法のどれを採用するか迷うかも知れないが,もっとも適切なのは,通常使っている Python を使うことであろうか。その場合には,以下の 3 行に示すように PyCall を再ビルドする必要がある(Python のバージョンが変わった場合には常に再ビルドした方が良い)。
# ENV["PYTHON"] = "/usr/local/bin/python3"
# using Pkg
# Pkg.build("PyCall")
なお,元の Julia 依存の Python を使う場合は,1行目を ENV["PYTHON"] = "" として PyCall を再ビルドすればよい。
また,再ビルドした場合は,一度 Julia を終了し,再度 Julia を立ち上げる必要がある。
なにはともあれ,Python の scipy.optimize がインポートできれば,そのなかの newton 関数を使えると言うことである。
so.newton(x -> cos(x) - x, 1) # 0.7390851332151607
pycall で Python の関数を呼ぶ
pycall(func::PyObject, returntype::Type, args...)
@pycall func(args...)::returntype
math.sqrt(3.0)
pycall(math.sqrt, PyAny, 3.0) # 1.7320508075688772
pycall(math.sqrt, Float64, 3.0) # 1.7320508075688772
@pycall math.sqrt(3.0)::Float64 # 1.7320508075688772
using Random; Random.seed!(12345)
x = randn(100);
sum(x) # 0.9821131440360702 Julia の sum 関数
pycall(np.sum, PyAny, x) # 0.9821131440360702
np.sum(x) # 0.9821131440360702
前の記事にも書いたが,py"...", py"""...""" で ... の部分に書かれた Python プログラムを実行できる。
using PyCall
py"""
import numpy as np
def f(x):
return x**2
"""
py"f(5)" # 25
py"\"hello\"" # py"print(\"hello\")"
"""...""" は結果を返さない(後述)。
py"print(\"hello\")"
py"""
print(9)
"""
Julia 中での変数などを PyCall で参照するのは「$変数名」でできる(RCall と同じ)。
また,PyCall の結果も,Julia の変数に代入できる。
using PyCall
以下の例は,Julia の配列 [1,2,3] を Python の sum 関数で和をとる例である。
py"sum($([1,2,3]))" # 6
配列 x を $x で参照する。
x = [1, 2, 3]
py"sum($x)" # 6
Julia の文字列変数は $y, $$y のようにして使用できる。
y = "1+2+3"
$y は文字列そのまま使用される
py"$y" # "1+2+3"
$$y は文字列を評価して使用される
py"$$y" # 6
直接文字列を記載する場合は \ でエスケープする。
py"$$(\"1+2+3\")" # 6
以下の例で,sqrt(8)+1 は Python の式として評価される。従って sqrt() という関数はないのでエラーになる。
z = "sqrt(8)+1"
py"$$z" # PyError
math パッケージをインポートしてもエラーになる。結局,解決法はまだわからない。
math = pyimport("math")
当然,以下は正しく実行される。
math.sqrt(8)+1 # 3.8284271247461903
z = "math.sqrt(8)+1" # "math.sqrt(8)+1"
py"$z" # "math.sqrt(8)+1"
py"$$z" # PyError
引き渡される文字列は「Python の式」であることに注意が必要。
以下は「2 の 4 乗から 3 を引く」つまり結果として 13 を得る。
z = "2**4-3"
py"$$z" # 13
以下は「2 と 4 のビット和から 3 を引く」。つまり 0b010 と 0b100 のビット和 0b110 = 6 から 3 を引くので,結果は 3 になる。
z = "2^4-3"
py"$$z" # 3
前にも述べたが,"""...""" は結果を返さない。
py"""
x = 2
y = 3
x ** y
"""
Julia に結果を返す変数を global 指定すると後で引用できる。
py"""
global g
x = 2
y = 3
g = x ** y
"""
py"g" # 8
a = py"g" # 8
a # 8