MATLAB是一款數值和矩陣計算軟體,兼有強大的時域系統以及電力模擬Simulink模塊,這使得MATLAB在工程領域有著難以取代的地位。不過受限於面向過程的開發邏輯,較大的體積和繁瑣的安裝、破解流程,以及正版昂貴的特性,加之並不太活躍的官方以及社區支持,對於普通用戶和數據分析用戶,以及開發項目的純程序員一直不友好,MATLAB在編程語言界的地位也一直不太高,且有逐年下降的趨勢。相比之下,Python具有體積小巧,第三方庫包多,社區數量多且用戶活躍度高的優點,許多大學和機構都有Python的支持和開發項目。Python也由於其igraph包豐富的繪圖能力而被許多視覺處理以及圖論方向的學者青睞。不過Python對於矩陣和向量運算的格式要求較高,即便是數值矩陣計算模塊numpy也不如Matlab靈活,運算速度也不如MATLAB快。不過這兩種語言同為腳本語言,語法上也有諸多的相似,精通一種語言的人上手另一種起來還是相對較快的。
出於各種考慮,Python和MATLAB中都已經添加了對方的函數介面,可以在MATLAB腳本中直接調用Python函數,Python代碼中也可以直接調用MATLAB函數和變數,其便捷程度幾乎與直接在Python IDLE或是MATLAB工作區中調用一樣。為了結合兩種編程語言在工程領域和非工程項目領域各自的優點,學習調用對方函數的方法是很有必要的。
一、MATLAB中PYTHON介面的調用
1.MATLAB中安裝Python介面
MATLAB中調用Python十分簡單,只需要本機安裝了Python,在安裝Python時需要將Python添加到命令行的默認目錄下,然後將Python編譯器python.exe的目錄添加到MATLAB的「設置路徑」中即可,就像添加其他toolbox一樣簡單。
比如,我將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中一樣的輸出結果。
需要注意的是,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(僅指速度),不過那個語言真是太不簡便了。