Julia:调用python函数的几种方法

79 篇文章 6 订阅

几年前尝试过用julia调用python代码(函数),虽然都用PyCall,看起来很方便,但是系统性的总结却不多。
本文主要参考资料:

https://github.com/JuliaPy/PyCall.jl

1、julia调用python现成库中函数

# 调用python中现有的库
using PyCall
math = pyimport("math")
v = math.sin(math.pi / 4) # returns ≈ 1/√2 = 0.70710678...
println("v:",v)
using PyCall
@pyimport numpy as np
@pyimport matplotlib.pyplot as plt

x=np.linspace(0,2pi,1000)
y=np.sin(3x+4*np.cos(2x))

plt.plot(x,y)
plt.show()

2、直接在Julia中写python代码
下面代码在:julia_call_py.jl中

module MyModule

using PyCall

function __init__()
    py"""
    import numpy as np

    def one(x):
        return np.sin(x) ** 2 + np.cos(x) ** 2
    def hello_world(s):
        return s +" hello world!"
    """
end

two(x) = py"one"(x) + py"one"(x)
helloworld(s) = py"hello_world"(s)

end

调用法:

julia> include("julia_call_py.jl")
Main.MyModule

julia> MyModule.two(2)
2.0
julia> MyModule.helloworld("julia")
"julia hello world!"

即可以直接julia中运行。

3、julia调用现成手写的python代码
此时的情况不一样,需要进行特别处理。第一,需要把python代码文件加载进python的工作目录中。
本质上是,sys.path.append(“路径名…”).

在我这里,python代码文件路径是“D:\py_joinquant\julia.py”,和julia代码并不在一个工程目录内。
需要用到pushfirst!函数。

那么路径目录是“D:\py_joinquant\”。

julia> pushfirst!(PyVector(pyimport("sys")."path"), "D:\\py_joinquant\\")
PyObject ['D:\\py_joinquant\\', '', '', 'C:\\Users\\songroom\\.julia\\conda\\3\\python37.zip', 'C:\\Users\\songroom\\.julia\\conda\\3\\DLLs', 'C:\\Users\\songroom\\.julia\\conda\\3\\lib', 'C:\\Users\\songroom\\AppData\\Local\\Programs\\Julia-1.6.0\\bin', 'C:\\Users\\songroom\\.julia\\conda\\3', 'C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages', 'C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\win32', 'C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\win32\\lib', 'C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\Pythonwin']

这样,python就可以找到相应的路径了。这时,你可以看到,sys.path:

julia> pyimport("sys").path
13-element Vector{String}:
 "D:\\py_joinquant\\"
 ""
 ""
 "C:\\Users\\songroom\\.julia\\conda\\3\\python37.zip"
 "C:\\Users\\songroom\\.julia\\conda\\3\\DLLs"
 "C:\\Users\\songroom\\.julia\\conda\\3\\lib"
 "C:\\Users\\songroom\\AppData\\Local\\Programs\\Julia-1.6.0\\bin" 
 "C:\\Users\\songroom\\.julia\\conda\\3"
 "C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages"       
 "C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\win32"
 "C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\win32\\lib"
 "C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\Pythonwin"

另外,julia.py文件如下:

import logging as log
def get_corecode_from_code(code):
    # IC1906.CCFX=>IC
    # IC1906=>IC
    # 600036=>600036
    # 600036.XSHE =>600036
    length = len(code)
    if length <2:
        log.info("get_corecode_from_code=> code %s may be wrong!",code)
        return ""
    code_first = code.split('.')
    s = code_first[0]
    if len(s)==1:
        return s.upper()
    if s[0].isalpha(): #futures
        
        if  s[1].isdigit():
            return s[0].upper()
        else:
            return s[:2].upper()
    else:
        return s.upper()

引进julia.py文件,并进行调用:

julia> @pyimport julia

julia> julia.get_corecode_from_code("600036")
"600036"

julia> julia.get_corecode_from_code("IC1906.CCFX")
"IC"

julia> pyimport("sys").path
13-element Vector{String}:
 "D:\\py_joinquant\\tools.py"
 "D:\\py_joinquant\\"
 ""
 ""
 "C:\\Users\\songroom\\.julia\\conda\\3\\python37.zip"
 "C:\\Users\\songroom\\.julia\\conda\\3\\DLLs"
 "C:\\Users\\songroom\\.julia\\conda\\3\\lib"
 "C:\\Users\\songroom\\AppData\\Local\\Programs\\Julia-1.6.0\\bin"
 "C:\\Users\\songroom\\.julia\\conda\\3"
 "C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages"
 "C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\win32"
 "C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\win32\\lib"
 "C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\Pythonwin"

julia> julia.get_corecode_from_code("60")
"60"

综合起来,其实还是比较简单的:

pushfirst!(PyVector(pyimport("sys")."path"), "D:\\py_joinquant\\") 
@pyimport julia
julia.get_corecode_from_code("600036")

例二:julia调用sklearn中机器学习相关库
有一个sklearn_test.py,路径如下:

C:\Users\songroom\Desktop\sklearn_test.py
from sklearn import datasets
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt

def LR_predict():
    #使用以后的数据集进行线性回归
    loaded_data=datasets.load_boston()
    data_X=loaded_data.data
    data_y=loaded_data.target

    model=LinearRegression()
    model.fit(data_X,data_y)

    print(model.predict(data_X[:4,:]))
    print(data_y[:4])

    #参数
    print(model.coef_)      #如果y=0.1x+0.3   则此行输出的结果为0.1
    print(model.intercept_)             #此行输出的结果为0.3
    print(model.get_params())       #模型定义时定义的参数,如果没有定义则返回默认值
    print(model.score(data_X,data_y))   #给训练模型打分,注意用在LinearR中使用R^2 conefficient of determination打分
    return model.coef_

julia调用LR_predict()函数:此时会报这个错误!

julia> @pyimport sklearn_test
ERROR: PyError (PyImport_ImportModule

The Python package sklearn_test could not be imported by pyimport. Usually this means
that you did not install sklearn_test in the Python version being used by PyCall.

PyCall is currently configured to use the Julia-specific Python distribution
installed by the Conda.jl package.  To install the sklearn_test module, you can
use `pyimport_conda("sklearn_test", PKG)`, where PKG is the Anaconda
package the contains the module sklearn_test, or alternatively you can use the
Conda package directly (via `using Conda` followed by `Conda.add` etcetera).

Alternatively, if you want to use a different Python distribution on your
system, such as a system-wide Python (as opposed to the Julia-specific Python),
you can re-configure PyCall with that Python.   As explained in the PyCall
documentation, set ENV["PYTHON"] to the path/name of the python executable
you want to use, run Pkg.build("PyCall"), and re-launch Julia.

) <class 'ModuleNotFoundError'>
ModuleNotFoundError("No module named 'sklearn'")
  File "C:\Users\songroom\Desktop\sklearn_test.py", line 1, in <module>
    from sklearn import datasets

Stacktrace:
 [1] pyimport(name::String)
   @ PyCall ~\.julia\packages\PyCall\BD546\src\PyCall.jl:550
 [2] top-level scope
   @ ~\.julia\packages\PyCall\BD546\src\PyCall.jl:594

此时,需要通过Conda.add()是可以解决的。

另外,需要注意的是,sklearn库在add时名称是全称。

julia> using Conda
julia> Conda.add("scikit-learn") 

julia> pyimport("sklearn")
PyObject <module 'sklearn' from 'C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\sklearn\\__init__.py'>

其它操作如下:

julia> using PyCall

julia> pushfirst!(PyVector(pyimport("sys")."path"), "C:\\Users\\songroom\\Desktop\\")
PyObject ['C:\\Users\\songroom\\Desktop\\', 'C:\\Users\\songroom\\.julia\\conda\\3\\python37.zip', 'C:\\Users\\songroom\\.julia\\conda\\3\\DLLs', 'C:\\Users\\songroom\\.julia\\conda\\3\\lib', 'C:\\Users\\songroom\\AppData\\Local\\Programs\\Julia-1.6.0\\bin', 'C:\\Users\\songroom\\.julia\\conda\\3', 'C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages', 'C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\win32', 'C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\win32\\lib', 'C:\\Users\\songroom\\.julia\\conda\\3\\lib\\site-packages\\Pythonwin']

julia> @pyimport sklearn_test

julia> sklearn_test.LR_predict()
[30.00384338 25.02556238 30.56759672 28.60703649]
[24.  21.6 34.7 33.4]
[-1.08011358e-01  4.64204584e-02  2.05586264e-02  2.68673382e+00
 -1.77666112e+01  3.80986521e+00  6.92224640e-04 -1.47556685e+00
  3.06049479e-01 -1.23345939e-02 -9.52747232e-01  9.31168327e-03
 -5.24758378e-01]
36.459488385089855
{'copy_X': True, 'fit_intercept': True, 'n_jobs': None, 'normalize': False, 'positive': False}
0.7406426641094095
13-element Vector{Float64}:
  -0.10801135783679539
   0.04642045836687953
   0.020558626367068917
   2.6867338193448442
 -17.766611228299986
   3.8098652068092282
   0.0006922246403431768
  -1.47556684560025
   0.30604947898516427
  -0.012334593916574021
  -0.9527472317072921
   0.00931168327379375
  -0.5247583778554881

julia>

可以看到,julia调用python的sklearn库代码成功!

需要注意的是,在pyimport(“sklearn_test”)仍然成功的情况下,如果还说发现找不到相关的sklearn库,或sklearn_test文件,可能需要退出julia repl,重启,对操作进行初始化一下。

整体代码如下:

using PyCall
pushfirst!(PyVector(pyimport("sys")."path"), "C:\\Users\\songroom\\Desktop\\")
@pyimport sklearn_test
sklearn_test.LR_predict()

4、调用类中的方法
还是这个sklearn_test.py文件,目录不变:

C:\Users\songroom\Desktop\sklearn_test.py

此时,我们把原来的python中函数形式改成类中方法形式,如下:

from sklearn import datasets
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import numpy.polynomial

class MyNet(numpy.polynomial.Polynomial):
    def __init__(self, x=10):
        self.x = x
    def my_method(self, arg1):
         return arg1 + 20
    @property
    def x2(self): 
        return self.x * 2
    @x2.setter
    def x2(self, new_val):
        self.x = new_val / 2
    def add(self,a,b):
        return a+b
    def LR_predict(self):
        #使用以后的数据集进行线性回归
        loaded_data=datasets.load_boston()
        data_X=loaded_data.data
        data_y=loaded_data.target

        model=LinearRegression()
        model.fit(data_X,data_y)

        print(model.predict(data_X[:4,:]))
        print(data_y[:4])

        #参数
        print(model.coef_)      #如果y=0.1x+0.3   则此行输出的结果为0.1
        print(model.intercept_)             #此行输出的结果为0.3
        print(model.get_params())       #模型定义时定义的参数,如果没有定义则返回默认值
        print(model.score(data_X,data_y))   #给训练模型打分,注意用在LinearR中使用R^2 conefficient of determination打分
        return model.coef_

此时如何从julia中调用python中的MyNet.LR_predict()?完整的脚本代码如下:

using PyCall
pushfirst!(PyVector(pyimport("sys")."path"), "C:\\Users\\songroom\\Desktop\\")
@pyimport sklearn_test
my_net = sklearn_test.MyNet() 
my_net.LR_predict()

5、julia与python类的深度融合 : @pydef

借用PyCall.jl中的例子

using PyCall
P = pyimport("numpy.polynomial")
@pydef mutable struct Doubler <: P.Polynomial
    function __init__(self, x=10)
        self.x = x
    end
    my_method(self, arg1::Number) = arg1 + 20
    x2.get(self) = self.x * 2
    function x2.set!(self, new_val)
        self.x = new_val / 2
    end
end
value = Doubler().x2;
println("value :",value)

执行:

julia> @time include("julia_call_py.jl")
value :20
  0.276265 seconds (574.10 k allocations: 35.587 MiB, 3.07% gc time, 94.97% compilation time)

以上就是julia调用python中原生函数,类中方法的几种方式。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值