MATLAB中调用Python及其相关库(以igraph和numpy为例)

℡╲_俬逩灬. 提交于 2020-02-24 20:26:58

MATLAB是一款数值和矩阵计算软件,兼有强大的时域系统以及电力仿真Simulink模块,这使得MATLAB在工程领域有着难以取代的地位。不过受限于面向过程的开发逻辑,较大的体积和繁琐的安装、破解流程,以及正版昂贵的特性,加之并不太活跃的官方以及社区支持,对于普通用户和数据分析用户,以及开发项目的纯程序员一直不友好,MATLAB在编程语言界的地位也一直不太高,且有逐年下降的趋势。相比之下,Python具有体积小巧,第三方库包多,社区数量多且用户活跃度高的优点,许多大学和机构都有Python的支持和开发项目。Python也由于其igraph包丰富的绘图能力而被许多视觉处理以及图论方向的学者青睐。不过Python对于矩阵和向量运算的格式要求较高,即便是数值矩阵计算模块numpy也不如Matlab灵活,运算速度也不如MATLAB快。不过这两种语言同为脚本语言,语法上也有诸多的相似,精通一种语言的人上手另一种起来还是相对较快的。
出于各种考虑,Python和MATLAB中都已经添加了对方的函数接口,可以在MATLAB脚本中直接调用Python函数,Python代码中也可以直接调用MATLAB函数和变量,其便捷程度几乎与直接在Python IDLE或是MATLAB工作区中调用一样。为了结合两种编程语言在工程领域和非工程项目领域各自的优点,学习调用对方函数的方法是很有必要的。
(本文前提是您已经在Python中安装了igraph和numpy库)

一、MATLAB中PYTHON接口的调用

1.MATLAB中安装Python接口

MATLAB中调用Python十分简单,只需要本机安装了Python,在安装Python时需要将Python添加到命令行的默认目录下,然后将Python编译器python.exe的目录添加到MATLAB的“设置路径”中即可,就像添加其他toolbox一样简单。
添加Python路径
比如,我将Python安装在了E盘,python.exe处于E:\Python2\python.exe,只要将相应路径加入即可。

MATLAB命令行或脚本窗口中键入pyversion,检测是否添加成功:
检验是否添加成功
这表示Python在MATLAB中添加成功,可以调用Python及其安装的库。

2.基本的Python函数调用

如果是Python自带的库,直接用py.xxx()就行,例如用py.list()创建一个py.list变量,MATLAB命令行会直接输出与Python shell中一样的输出结果:
在这里插入图片描述
Python中的输出
Python中的输出

在这里插入图片描述
MATLAB中的输出
MATLAB中的输出

需要注意的是,MATLAB是可以自动转换自身类型的1×n向量的,如Python标准格式中,list每两个元素间要用逗号而不能用空格,而MATLAB则都可以,上面我们没有用逗号而是用了空格,MATLAB的Python接口就自动转换了这个格式问题。但是对于m×n矩阵MATLAB是不会自动转换的,需要人工转换为py.list或者py.numpy.ndarray等类型,后面会说明。

而且Python2的print不是一个函数因此没有()这个用法,而在MATLAB中则必须要加括号,否则系统会不认为这是一个函数而报错:
在这里插入图片描述

在这里插入图片描述

还有其它一些问题,比如Python的函数输入参数中是可以用"="来确定参数名称的,而在MATLAB中这个功能是用’ '来完成的,比如一个MATLAB函数func()有三个输入参数,名称为a b c,想直接输入a和c而b使用func()定义时的默认值,就可以像这样:

func('a',1,'c',0) %MATLAB代码

而类似的情况在Python中则需要像这样:

func(a=1,c=0) #Python代码

那么问题就来了,MATLAB中是不允许函数括号中出现单等号的,只能有双等号,即使是Python接口函数,这么输入也会报错,就像py.print()一样。
MATLAB中创建的Python对象可以像Python里一样采用对象函数的".“调用方法,如创建了一个igraph.Graph()对象,使用famous函数导入Zachary的空手道俱乐部数据,并用”."来调用自身的函数get_adjacency()来获取邻接矩阵:

ab=py.igraph.Graph.Famous("Zachary");
bc=ab.get_adjacency();  %MATLAB
py.print(ab);
bc

输出结构如下:

>> untitled2
IGRAPH U--- 34 78 --
+ edges:
 0 --  1  2  3  4  5  6  7  8 10 11 12 13 17 19 21 31
 1 --  0  2  3  7 13 17 19 21 30
 2 --  0  1  3  7  8  9 13 27 28 32
 3 --  0  1  2  7 12 13
 4 --  0  6 10
 5 --  0  6 10 16
 6 --  0  4  5 16
 7 --  0  1  2  3
 8 --  0  2 30 32 33
 9 --  2 33
10 --  0  4  5
11 --  0
12 --  0  3
13 --  0  1  2  3 33
14 -- 32 33
15 -- 32 33
16 --  5  6
17 --  0  1
18 -- 32 33
19 --  0  1 33
20 -- 32 33
21 --  0  1
22 -- 32 33
23 -- 25 27 29 32 33
24 -- 25 27 31
25 -- 23 24 31
26 -- 29 33
27 --  2 23 24 33
28 --  2 31 33
29 -- 23 26 32 33
30 --  1  8 32 33
31 --  0 24 25 28 32 33
32 --  2  8 14 15 18 20 22 23 29 30 31 33
33 --  8  9 13 14 15 18 19 20 22 23 26 27 28 29 30 31 32

bc = 

  Python Matrix - 属性:

     data: [1×34 py.list]
    shape: [1×2 py.tuple]

    [[0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
     [1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
     [1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0]
     [1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1]
     [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
     [1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
     [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
     [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
     [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1]
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0]
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0]
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1]
     [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1]
     [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1]
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1]
     [0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1]
     [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1]
     [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0]]

3. MATLAB中的Python库import

MATLAB中也可以像Python代码一样用import,只不过方法略有不同。在Python代码中,即时你已经像这样引入了igraph库,仍然需要输入库名才可以运行正确:

import igraph
g=igraph.Graph() #Python

当然,用from igraph import *可以解决这个问题。
而在MATLAB中,引用Python库及创建Graph对象的方法是:

import py.igraph.*;
g=Graph(); %MATLAB

无需再输入库名。当然我个人不推荐这个方法,因为这样在matlab中import会直接覆盖MATLAB中的同名函数,比如

import py.numpy.*;
aa=[1,2,3;
4,5,6;];   % MATLAB
reshape(aa,1,1*3)

这样就会报错,因为aa是一个MATLAB 2×3矩阵,而此时的reshape已经是Python numpy库中的函数了,因为之前已经import了。当然可以用clear import解决这个问题,退出numpy库,但这样做还是很容易引起混淆。如果要用Python的numpy库里的reshape(),建议不要事先import而是这样:

py.numpy.reshape() %MATLAB

其他库和函数同理。

二、MATLAB到PYTHON的数据格式转换

为了方便,本节中表格以外的部分有些地方的Python数据类型都以py.xxx命名,如Python list数据类型就是py.list,而没有py.前缀或上下文特指的则全部是MATLAB数据类型,以避免混淆。

1.单个数据及向量的转换

Matlab官方在开发Python接口函数时,考虑到了一些类型转换的问题。官方提供的Matlab和Python之间的类型自动转换如下:
MATLAB-Python接口数据转换官方链接

MATLAB 输入参数类型 生成的 Python py. 类型
double / single float
复数 single / 复数 double complex
int8 / uint8 / int16 / uint16 / int32 int
uint32 / int64 / uint64 int / long(仅限 2.7 版本)
NaN float(nan)
Inf float(inf)
string / char str
logical bool
struct dict
double 型1×N向量 array.array(‘d’)
single 型1×N向量 array.array(‘f’)
int8 型1×N向量 array.array(‘b’)
uint8 型1×N向量 array.array(‘B’)
int16 型1×N向量 array.array(‘h’)
uint16 型1×N向量 array.array(‘H’)
int32 型1×N向量 array.array(‘i’)
uint32 型1×N向量 array.array(‘I’)
int64 型1×N向量 (不支持Windows上的Python 2.7) array.array(‘q’)
uint64 型1×N向量 (不支持Windows上的Python 2.7) array.array(‘Q’)
char 型1×N向量,含大于127 (仅支持Python 2.7) unicode
logical型1×N向量 memoryview
cell 型1×N向量 tuple

需要注意的是,MATLAB仅能自动转换1×N的向量,对于m×n的矩阵则不会自动转换。在一些需要输入类型为numpy的ndarray或是list数据类型的情况下,Python接口函数会报错。比如这样一段简单的MATLAB-Python代码,调用Python的igraph库,利用加权邻接矩阵生成Graph变量:

AA=[1,1,1;1,1,1;1,1,1;];
h0=py.igraph.Graph.Weighted_Adjacency(AA,ADJ_UNDIRECTED);

会有如下的错误提示:

错误使用 py.igraph.Graph.Weighted_Adjacency
Python 函数 "Weighted_Adjacency" 无法接受位置 1 处的至少一个输入参数。该函数可能需要您可以从 MATLAB 数组构造的特定数据类型。有关详细信
息,请参阅关于 Python 函数 "Weighted_Adjacency" 和使用 Python 数组的文档。

出错 untitled2 (line 2)
h0=py.igraph.Graph.Weighted_Adjacency(AA,ADJ_UNDIRECTED);

因为igraph.Graph.Weighted_Adjacency()需要一个输入类型为py.list的矩阵,因此MATLAB矩阵它完全不认识。天真的我直接在MATLAB中使用了Python里常用的数据类型转换函数:

AA=[1,1,1;1,1,1;1,1,1;];
AA=py.list(AA);%%此代码不能运行!!!!!!
h0=py.igraph.Graph.Weighted_Adjacency(AA,ADJ_UNDIRECTED);

这第二行代码直接把我的电脑搞死机了,鼠标都动不了那种,试了两次都这样。不知道是不是我电脑或者MATLAB版本或者Python的问题,我用的是MATLAB2018b,Python是2.7.17,胆子大的同学可以试一下,这样是不是也会死机。

2.多维数组(矩阵)的转换

MATLAB官方压根没有考虑矩阵的转换问题。类似list这样的矩阵会被转换为cell,dict和tuple也是。而直接用cell2mat还不太行。在网上找了很多方法,最后解决了。下面给出一个MATLAB矩阵转换为py.list矩阵的函数:

%% 输入:matlab矩阵
%% 输出:python list类型矩阵
function AA3=mat2py_list(AA0)
len1=size(AA0,1);
len2=size(AA0,2);
AA=reshape(AA0,1,len1*len2);
AA1=py.list(AA);
AA2=py.numpy.reshape(AA1,[int8(len1),int8(len2)]);
AA3=py.list(AA2);
end

注意reshape里的后两个变量要用int8()框起来,这是matlab中的强制类型转换函数。由于numpy.reshape要求这两个index为py.int型,而在matlab中输入的一个数字默认是double型,因此matlab会通过Python接口自动转换为py.float型,因此会报错。用表中其他的matlab数据类型都可以,如int32 int16,只要能自动转换为py.int型。

三、在MATLAB中指定Python函数的变量名

在Python中,那些具有非常多的输入变量的函数通常都会在调用函数的时候指定变量名。如igraph包中,要用一个list矩阵(全为正值,对角线全0,且关于对角线对称)生成一个有权网络,通常都会这样:

######################
import igraph
ADJ_UNDIRECTED=igraph.ADJ_UNDIRECTED
w_adjac=list([[0,1,2],[1,0,3],[2,3,0])
w_graph=igraph.Graph.WeightedAdjacency(w_adjac,mode=ADJ_UNDIRECTED)
#######################

在这个函数中"mode="其实可以省略,但如果可供选择的输入变量特别多,比如有6-7个,而mode这个变量在原函数定义的时候不是第二个位置的变量,你又只想声明一项,就不能省略了,否则编译器会将这个变量值安排在本应在位置2处的变量,这样就会发生错误。其实在matlab中也有类似的情况,不过matlab在函数括号不能出现单等号,所以都是如下形式(以rand()为例):

%%%%%%%%%%%%%%%%
rand('state',0)
%%%%%%%%%%%%%%%%

意思是把原函数定义里叫state的变量赋值0,至于原函数中处于哪个位置则不用管。既然matlab函数括号里不能有单等号那怎么办呢?matlab也有办法,不过比较繁琐,那就是用pyargs函数,开发人员还是比较贴心的。比如上面那段python代码可以在matlab里这样表示:

%%%%%%%%%%%%
ADJ_UNDIRECTED=py.igraph.ADJ_UNDIRECTED;
w_adjac=mat2py_list([0,1,2;1,0,3;2,3,0]); %参照上一节的函数
w_graph=py.igraph.Graph.WeightedAdjacency(w_adjac,pyargs('mode',ADJ_UNDIRECTED));
%%%%%%%%%%%%

可以与上面的Python代码形成一样的效果。

四、总结

其实可以发现在MATLAB里调用还是比较麻烦的,有各种各样的小坑,而且在数值计算和矩阵向量方面MATLAB无论是速度还是代码简洁度都能甩Python的Numpy等库好几条街。。。只有在图论方面比MATLAB略有优势。可能会有做金融和数据统计的人会喜欢在MATLAB里调用Python,工程和科学计算还是MATLAB强,当然最厉害的还是FORTRAN(仅指速度),不过那个语言真是太不简便了

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!