摘要:NumPy是Python的最重要的擴展程序庫之一,也是入門機器學習編程的必備工具。國外有位程序員講NumPy的基本運算以圖解的方式寫下來,讓學習過程變得輕鬆有趣。
NumPy是Python的最重要的擴展程序庫之一,也是入門機器學習編程的必備工具。然而對初學者來講,NumPy的大量運算方法很是難記。程序員
最近,國外有位程序員講NumPy的基本運算以圖解的方式寫下來,讓學習過程變得輕鬆有趣。在Reddit機器學習社區發佈不到半天就收穫了500+贊。編程
下面就讓咱們跟隨他的教程一塊兒來學習吧!數組
教程內容分爲向量 (一維數組)、矩陣 (二維數組)、三維與更高維數組3個部分。app
Numpy數組與Python列表
在介紹正式內容以前,先讓咱們先來了解一下Numpy數組與Python列表的區別。機器學習
乍一看,NumPy數組相似於Python列表。它們均可以用做容器,具備獲取(getting)和設置(setting)元素以及插入和移除元素的功能。函數
二者有不少類似之處,如下是兩者在運算時的一個示例:工具
和Python列表相比,Numpy數組具備如下特色:佈局
更緊湊,尤爲是在一維以上的維度;向量化操做時比Python列表快,但在末尾添加元素比Python列表慢。學習
\測試
△在末尾添加元素時,Python列表複雜度爲O(1),NumPy複雜度爲O(N)
向量運算
向量初始化
建立NumPy數組的一種方法是從Python列表直接轉換,數組元素的類型與列表元素類型相同。
NumPy數組沒法像Python列表那樣加長,由於在數組末尾沒有保留空間。
所以,常見的作法是定義一個Python列表,對它進行操做,而後再轉換爲NumPy數組,或者用np.zeros和np.empty初始化數組,預分配必要的空間:
有時咱們須要建立一個空數組,大小和元素類型與現有數組相同:
實際上,全部用常量填充建立的數組的函數都有一個_like對應項,來建立相同類型的常數數組:
在NumPy中,能夠用arange或者linspace來初始化單調序列數組:
若是須要相似[0., 1., 2.]的浮點數組,能夠更改arange輸出的類型:arange(3).astype(float)。
可是有更好的方法:arange函數對數據類型敏感,若是將整數做爲參數,生成整數數組;若是輸入浮點數(例如arange(3.)),則生成浮點數組。
可是arange在處理浮點數方面並非特別擅長:
這是由於0.1對於咱們來講是一個有限的十進制數,但對計算機而言卻不是。在二進制下,0.1是一個無窮小數,必須在某處截斷。
這就是爲何將小數部分加到步驟arange一般是一個不太好的方法:咱們可能會遇到一個bug,致使數組的元素個數不是咱們想要的數,這會下降代碼的可讀性和可維護性。
這時候,linspace會派上用場。它不受舍入錯誤的影響,並始終生成要求的元素數。
出於測試目的,一般須要生成隨機數組,NumPy提供隨機整數、均勻分佈、正態分佈等幾種隨機數形式:
向量索引
一旦將數據存儲在數組中,NumPy便會提供簡單的方法將其取出:
上面展現了各式各樣的索引,例如取出某個特定區間,從右往左索引、只取出奇數位等等。
但它們都是所謂的view,也就是不存儲原始數據。而且若是原始數組在被索引後進行更改,則不會反映原始數組的改變。
這些索引方法容許分配修改原始數組的內容,所以須要特別注意:只有下面最後一種方法纔是複製數組,若是用其餘方法均可能破壞原始數據:
從NumPy數組中獲取數據的另外一種超級有用的方法是布爾索引,它容許使用各類邏輯運算符,來檢索符合條件的元素:
注意:Python中的三元比較3<=a<=5在NumPy數組中不起做用。
如上所述,布爾索引也會改寫數組。它有兩個常見的函數,分別是np.where和np.clip:
向量運算
算術運算是NumPy速度最引入注目的地方之一。NumPy的向量運算符已達到C++級別,避免了Python的慢循環。
NumPy容許像普通數字同樣操做整個數組(加減乘除、整除、冪):
△ 和Python中同樣,a//b表示div b(整除),x**n表示xⁿ
向量還能夠與標量進行相似的運算,方法相同:
大多數的數學函數都有NumPy對應項用於處理向量:
向量的點積、叉積也有運算符:
咱們也能夠進行三角函數、反三角函數、求斜邊運算:
數組能夠四捨五入爲整數:
△ floor取下界;ceil取上界;round爲四捨六入五取偶
NumPy還能夠執行如下基本的統計運算(最大最小值、平均值、方差、標準差):
不過排序函數的功能比Python列表對應函數更少:
搜索向量中的元素
與Python列表相反,NumPy數組沒有index方法。
- 查找元素的一種方法是np.where(a==x)[0][0],它既不優雅也不快速,由於要查找的項須要從開頭遍歷數組的全部元素。
- 更快的方式是經過Numba中的next((i[0] for i, v in np.ndenumerate(a) if v==x), -1)來加速。
- 一旦對數組進行排序,狀況就會變得更好:v = np.searchsorted(a, x); return v if a[v]==x else -1的複雜度爲O(log N),確實很是快,可是首先須要O(N log N)的排序時間。
比較浮點數
函數np.allclose(a, b)用於比較具備給定公差的浮點數組:
- np.allclose假設全部的比較數字的等級是1個單位。例如在上圖中,它就認爲1e-9和2e-9相同,若是要進行更細緻的比較,須要經過atol指定比較等級1:np.allclose(1e-9, 2e-9, atol=1e-17) == False。
- math.isclose進行比較沒有假設前提,而是基於用戶給出的一個合理abs_tol值:math.isclose(0.1+0.2–0.3, abs_tol=1e-8) == True。
除此以外np.allclose在絕對和相對公差公式中還存在一些小問題,例如,對某些數存在allclose(a, b) != allclose(b, a)。這些問題已在math.isclose函數中獲得解決。
矩陣運算
NumPy中曾經有一個專用的類matrix,但如今已棄用,所以下面將交替使用矩陣和2D數組兩個詞。
矩陣初始化語法與向量類似:
這裏須要雙括號,由於第二個位置參數是爲dtype保留的。
隨機矩陣的生成也相似於向量的生成:
二維索引語法比嵌套列表更方便:
和一維數組同樣,上圖的view表示,切片數組實際上並未進行任何複製。修改數組後,更改也將反映在切片中。
axis參數
在許多操做(例如求和)中,咱們須要告訴NumPy是否要跨行或跨列進行操做。爲了使用任意維數的通用表示法,NumPy引入了axis的概念:axis參數其實是所討論索引的數量:第一個索引是axis=0,第二個索引是axis=1,等等。
所以在二維數組中,若是axis=0是按列,那麼axis=1就是按行。
矩陣運算
除了普通的運算符(如+,-,*,/,//和**)以元素方式計算外,還有一個@運算符可計算矩陣乘積:
在第一部分中,咱們已經看到向量乘積的運算,NumPy容許向量和矩陣之間,甚至兩個向量之間進行元素的混合運算:
行向量與列向量
從上面的示例能夠看出,在二維數組中,行向量和列向量被不一樣地對待。
默認狀況下,一維數組在二維操做中被視爲行向量。所以,將矩陣乘以行向量時,可使用(n,)或(1,n),結果將相同。
若是須要列向量,則有轉置方法對其進行操做:
可以從一維數組中生成二位數組列向量的兩個操做是使用命令reshape重排和newaxis創建新索引:
這裏的-1參數表示reshape自動計算第二個維度上的數組長度,None在方括號中充當np.newaxis的快捷方式,該快捷方式在指定位置添加了一個空axis。
所以,NumPy中總共有三種類型的向量:一維數組,二維行向量和二維列向量。這是二者之間顯式轉換的示意圖:
根據規則,一維數組被隱式解釋爲二維行向量,所以一般沒必要在這兩個數組之間進行轉換,相應區域用灰色標出。
矩陣操做
鏈接矩陣有兩個主要函數:
這兩個函數只堆疊矩陣或只堆疊向量時,均可以正常工做。可是當涉及一維數組與矩陣之間的混合堆疊時,vstack能夠正常工做:hstack會出現尺寸不匹配錯誤。
由於如上所述,一維數組被解釋爲行向量,而不是列向量。解決方法是將其轉換爲列向量,或者使用column_stack自動執行:
堆疊的逆向操做是分裂:
矩陣能夠經過兩種方式完成複製:tile相似於複製粘貼,repeat相似於分頁打印。
特定的列和行能夠用delete進行刪除:
逆運算爲插入:
append就像hstack同樣,該函數沒法自動轉置一維數組,所以再次須要對向量進行轉置或添加長度,或者使用column_stack代替:
實際上,若是咱們須要作的就是向數組的邊界添加常量值,那麼pad函數就足夠了:
Meshgrid
若是咱們要建立如下矩陣:
兩種方法都很慢,由於它們使用的是Python循環。在MATLAB處理這類問題的方法是建立一個meshgrid:
該meshgrid函數接受任意一組索引,mgrid僅是切片,indices只能生成完整的索引範圍。fromfunction如上所述,僅使用I和J參數一次調用提供的函數。
可是實際上,在NumPy中有一種更好的方法。無需在整個矩陣上耗費存儲空間。僅存儲大小正確的矢量就足夠了,運算規則將處理其他的內容:
在沒有indexing=’ij’參數的狀況下,meshgrid將更改參數的順序:J, I= np.meshgrid(j, i)—這是一種「 xy」模式,用於可視化3D圖。
除了在二維或三維數組上初始化外,meshgrid還能夠用於索引數組:
矩陣統計
就像以前提到的統計函數同樣,二維數組接受到axis參數後,會採起相應的統計運算:
二維及更高維度中,argmin和argmax函數返回最大最小值的索引:
all和any兩個函數也能使用axis參數:
矩陣排序
儘管axis參數對上面列出的函數頗有用,但對二維排序卻沒有幫助:
axis毫不是Python列表key參數的替代。不過NumPy具備多個函數,容許按列進行排序:
一、按第一列對數組排序:a[a[:,0].argsort()]
argsort排序後,此處返回原始數組的索引數組。
此技巧能夠重複,可是必須當心,以避免下一個排序混淆前一個排序的結果:
a = a[a[:,2].argsort()]a = a[a[:,1].argsort(kind=’stable’)]a = a[a[:,0].argsort(kind=’stable’)]
二、有一個輔助函數lexsort,該函數按上述方式對全部可用列進行排序,但始終按行執行,例如:
- a[np.lexsort(np.flipud(a[2,5].T))]:先經過第2列排序,再經過第5列排序;
- a[np.lexsort(np.flipud(a.T))]:按從左到右全部列依次進行排序。
三、還有一個參數order,可是若是從普通(非結構化)數組開始,則既不快速也不容易使用。
四、由於這個特殊的操做方式更具可讀性和它多是一個更好的選擇,這樣作的pandas不易出錯:
- pd.DataFrame(a).sort_values(by=[2,5]).to_numpy():經過第2列再經過第5列進行排序。
- pd.DataFrame(a).sort_values().to_numpy():經過從左向右全部列進行排序
高維數組運算
經過重排一維向量或轉換嵌套的Python列表來建立3D數組時,索引的含義爲(z,y,x)。
第一個索引是平面的編號,而後纔是在該平面上的移動:
這種索引順序很方便,例如用於保留一堆灰度圖像:這a[i]是引用第i個圖像的快捷方式。
可是此索引順序不是通用的。處理RGB圖像時,一般使用(y,x,z)順序:前兩個是像素座標,最後一個是顏色座標(Matplotlib中是RGB ,OpenCV中是BGR ):
這樣,能夠方便地引用特定像素:a[i,j]給出像素的RGB元組(i,j)。
所以,建立特定幾何形狀的實際命令取決於正在處理的域的約定:
顯然,NumPy函數像hstack、vstack或dstack不知道這些約定。其中硬編碼的索引順序是(y,x,z),RGB圖像順序是:
△RGB圖像數組(爲簡便起見,上圖僅2種顏色)
若是數據的佈局不一樣,則使用concatenate命令堆疊圖像,並在axis參數中提供顯式索引數會更方便:
若是不方便使用axis,能夠將數組轉換硬編碼爲hstack的形式:
這種轉換沒有實際的複製發生。它只是混合索引的順序。
混合索引順序的另外一個操做是數組轉置。檢查它可能會讓咱們對三維數組更加熟悉。
根據咱們決定的axis順序,轉置數組全部平面的實際命令將有所不一樣:對於通用數組,它交換索引1和2,對於RGB圖像,它交換0和1:
有趣的是,(和惟一的操做模式)默認的axes參數顛倒了索引順序,這與上述兩個索引順序約定都不相符。
最後,還有一個函數,能夠在處理多維數組時節省不少Python循環,並使代碼更簡潔,這就是愛因斯坦求和函數einsum:
它將沿重複索引的數組求和。
最後,若要掌握NumPy,能夠前去GitHub上的項目——100道NumPy練習題,驗證本身的學習成果。
本文分享自華爲雲社區《看圖學NumPy:掌握n維數組基礎知識點,看這一篇就夠了》,原文做者:HWCloudAI 。