譯者:AI研習(xí)社(季一帆)
雙語(yǔ)原文鏈接:NumPy Illustrated: The Visual Guide to NumPy
是一個(gè)廣泛適用的Python數(shù)據(jù)處理庫(kù),, 等庫(kù)都基于numpy。同時(shí),在、、等深度許欸小框架中,了解numpy將顯著提高數(shù)據(jù)共享和處理能力,甚至無(wú)需過(guò)多更改就可以在運(yùn)行計(jì)算。
n維數(shù)組是NumPy的核心概念,這樣的好處,盡管一維和而為數(shù)組的處理方式有些差異,但多數(shù)不同維數(shù)組的操作是一樣的。本文將對(duì)以下三個(gè)部分展開(kāi)介紹:
向量——一維數(shù)組
矩陣——二維數(shù)組
3維及更高維數(shù)組
本文受JayAlammar的文章“ A Visual Intro to NumPy”的啟發(fā),并對(duì)其做了更詳細(xì)豐富的介紹。
numpy數(shù)組 vs. Python列表
乍看上去,NumPy數(shù)組與Python列表極其相似。它們都用來(lái)裝載數(shù)據(jù),都能夠快速添加或獲取元素,插入和移除元素則比較慢。
當(dāng)然相比python列表,numpy數(shù)組可以直接進(jìn)行算術(shù)運(yùn)算:
除此之外,numpy數(shù)組還具有以下特點(diǎn):
更緊湊,高維時(shí)尤為明顯
向量化后運(yùn)算速度比列表更快
在末尾添加元素時(shí)不如列表高效
元素類(lèi)型一般比較固定
其中,O(N)表示完成操作所需的時(shí)間與數(shù)組大小成正比(請(qǐng)見(jiàn)),O(1)表示操作時(shí)間與數(shù)組大小無(wú)關(guān)(詳見(jiàn))。
1.向量與1維數(shù)組向量初始化
通過(guò)Python列表可以創(chuàng)建NumPy數(shù)組,如下將列表元素轉(zhuǎn)化為一維數(shù)組:
注意,確保列表元素類(lèi)型相同,否則dtype=’object‘,將影響運(yùn)算甚至產(chǎn)生語(yǔ)法錯(cuò)誤。
由于在數(shù)組末尾沒(méi)有預(yù)留空間以快速添加新元素,NumPy數(shù)組無(wú)法像Python列表那樣增長(zhǎng),因此,通常的做法是在變長(zhǎng)Python列表中準(zhǔn)備好數(shù)據(jù),然后將其轉(zhuǎn)換為NumPy數(shù)組,或是使用np.zeros或np.empty預(yù)先分配必要的空間:
通過(guò)以下方法可以創(chuàng)建一個(gè)與某一變量形狀一致的空數(shù)組:
不止是空數(shù)組,通過(guò)上述方法還可以將數(shù)組填充為特定值:
在NumPy中,還可以通過(guò)單調(diào)序列初始化數(shù)組:
如果您需要[0., 1., 2.]這樣的浮點(diǎn)數(shù)組,可以更改arange輸出的類(lèi)型,即arange(3).astype(float),但有更好的方法:由于arange函數(shù)對(duì)類(lèi)型敏感,因此參數(shù)為整數(shù)類(lèi)型,它生成的也是整數(shù)類(lèi)型,如果輸入float類(lèi)型arange(3.),則會(huì)生成浮點(diǎn)數(shù)。
arange浮點(diǎn)類(lèi)型數(shù)據(jù)不是非常友好:
上圖中,0.1對(duì)我們來(lái)說(shuō)是一個(gè)有限的十進(jìn)制數(shù),但對(duì)計(jì)算機(jī)而言,它是一個(gè)二進(jìn)制無(wú)窮小數(shù),必須四舍五入為一個(gè)近似值。因此,將小數(shù)作為arange的步長(zhǎng)可能導(dǎo)致一些錯(cuò)誤??梢酝ㄟ^(guò)以下兩種方式避免如上錯(cuò)誤:一是使間隔末尾落入非整數(shù)步數(shù),但這會(huì)降低可讀性和可維護(hù)性;二是使用linspace,這樣可以避免四舍五入的錯(cuò)誤影響,并始終生成要求數(shù)量的元素。但使用linspace時(shí)尤其需要注意最后一個(gè)的數(shù)量參數(shù)設(shè)置,由于它計(jì)算點(diǎn)數(shù)量,而不是間隔數(shù)量,因此上圖中數(shù)量參數(shù)是11,而不是10。
隨機(jī)數(shù)組的生成如下:
向量索引
對(duì)于數(shù)組數(shù)據(jù)的訪問(wèn),numpy提供了便捷的訪問(wèn)方式:
上圖中,除“fancy indexing”外,其他所有索引方法本質(zhì)上都是`views`:它們并不存儲(chǔ)數(shù)據(jù),如果原數(shù)組在被索引后發(fā)生更改,則會(huì)反映出原始數(shù)組中的更改。
上述所有這些方法都可以改變?cè)紨?shù)組,即允許通過(guò)分配新值改變?cè)瓟?shù)組的內(nèi)容。這導(dǎo)致無(wú)法通過(guò)切片來(lái)復(fù)制數(shù)組:
此外,還可以通過(guò)布爾索引從NumPy數(shù)組中獲取數(shù)據(jù),這意味著可以使用各種邏輯運(yùn)算符:
和與其他Python使用類(lèi)似
注意,不可以使用`3 《= a 《= 5`這樣的Python“三元”比較。
如上所述,布爾索引是可寫(xiě)的。如下圖np.where和np.clip兩個(gè)專(zhuān)有函數(shù)。
向量操作
NumPy的計(jì)算速度是其亮點(diǎn)之一,其向量運(yùn)算操作接近C++級(jí)別,避免了Python循環(huán)耗時(shí)較多的問(wèn)題。NumPy允許像普通數(shù)字一樣操作整個(gè)數(shù)組:
在python中,a//b表示a div b(除法的商),x**n表示 x?
浮點(diǎn)數(shù)的計(jì)算也是如此,numpy能夠?qū)?biāo)量廣播到數(shù)組:
numpy提供了許多數(shù)學(xué)函數(shù)來(lái)處理矢量:
向量點(diǎn)乘(內(nèi)積)和叉乘(外積、向量積)如下:
numpy也提供了如下三角函數(shù)運(yùn)算:
數(shù)組整體進(jìn)行四舍五入:
floor向上取整,ceil向下取整,round四舍五入
np.around與np.round是等效的,這樣做只是為了避免 from numpy import *時(shí)與Python aroun的沖突(但一般的使用方式是import numpy as np)。當(dāng)然,你也可以使用a.round。
numpy還可以實(shí)現(xiàn)以下功能:
以上功能都存在相應(yīng)的nan-resistant變體:例如,等
在numpy中,排序函數(shù)功能有所閹割:
對(duì)于一維數(shù)組,可以通過(guò)反轉(zhuǎn)結(jié)果來(lái)解決reversed函數(shù)缺失的不足,但在2維數(shù)組中該問(wèn)題變得棘手。
查找向量中的元素
不同于Python列表,NumPy數(shù)組沒(méi)有索引方法。
index中的方括號(hào)表示j或i&j可以省略
可以通過(guò)np.where(a==x)[0] [0]查找元素,但這種方法很不pythonic,哪怕需要查找的項(xiàng)在數(shù)組開(kāi)頭,該方法也需要遍歷整個(gè)數(shù)組。
使用Numba實(shí)現(xiàn)加速查找,next((i[0] for i, v in np.ndenumerate(a) if v==x), -1),在最壞的情況下,它的速度要比where慢。
如果數(shù)組是排好序的,使用v = np.(a, x); return v if a[v]==x else -1時(shí)間復(fù)雜度為O(log N),但在這之前,排序的時(shí)間復(fù)雜度為O(N log N)。
實(shí)際上,通過(guò)C實(shí)現(xiàn)加速搜索并不是困難,問(wèn)題是浮點(diǎn)數(shù)據(jù)比較。
浮點(diǎn)數(shù)比較
np.allclose(a, b)用于容忍誤差之內(nèi)的浮點(diǎn)數(shù)比較。
np.allclose假定所有比較數(shù)字的尺度為1。如果在納秒級(jí)別上,則需要將默認(rèn)atol參數(shù)除以1e9:np.allclose(1e-9,2e-9, atol=1e-17)==False。
math.isclose不對(duì)要比較的數(shù)字做任何假設(shè),而是需要用戶(hù)提供一個(gè)合理的abs_tol值(np.allclose默認(rèn)的atol值1e-8足以滿(mǎn)足小數(shù)位數(shù)為1的浮點(diǎn)數(shù)比較,即math.isclose(0.1+0.2–0.3, abs_tol=1e-8)==True。
此外,對(duì)于絕隊(duì)偏差和相對(duì)偏差,np.allclose依然存在一些問(wèn)題。例如,對(duì)于某些值a、b, allclose(a,b)!=allclose(b,a),而在math.isclose中則不存在這些問(wèn)題。查看GitHub上的和相應(yīng)的了解更多信息。
2.矩陣和二維數(shù)組
過(guò)去,NumPy中曾有一個(gè)專(zhuān)用的matrix類(lèi),但現(xiàn)在已被棄用,因此在下文中矩陣和2維數(shù)組表示同一含義。
矩陣的初始化語(yǔ)法與向量類(lèi)似:
如上要使用雙括號(hào),因?yàn)榈诙€(gè)位置參數(shù)(可選)是為dtype(也接受整數(shù))保留的。
隨機(jī)矩陣的生成也與向量類(lèi)似:
二維數(shù)組的索引語(yǔ)法要比嵌套列表更方便:
“view”表示數(shù)組切片時(shí)并未進(jìn)行任何復(fù)制,在修改數(shù)組后,相應(yīng)更改也將反映在切片中。
軸參數(shù)
在求和等操作中,NumPy可以實(shí)現(xiàn)跨行或跨列的操作。為了適用任意維數(shù)的數(shù)組,NumPy引入了axis的概念。axis參數(shù)的值實(shí)際上就是維度數(shù)量,如第一個(gè)維是axis=0 ,第二維是axis=1,依此類(lèi)推。因此,在2維數(shù)組中,axis=0指列方向,axis=1指行方向。
矩陣運(yùn)算
除了+,-,*,/,//和**等數(shù)組元素的運(yùn)算符外,numpy提供了@運(yùn)算符計(jì)算矩陣乘積:
類(lèi)似前文介紹的標(biāo)量廣播機(jī)制,numpy同樣可以通過(guò)廣播機(jī)制實(shí)現(xiàn)向量與矩陣,或兩個(gè)向量之間的混合運(yùn)算:
注意,上圖最后一個(gè)示例是對(duì)稱(chēng)的逐元素乘法。使用矩陣乘法@可以計(jì)算非對(duì)稱(chēng)線性代數(shù)外積,兩個(gè)矩陣互換位置后計(jì)算內(nèi)積:
行向量與列向量
根據(jù)前文可知,在2維數(shù)組中,行向量和列向量被區(qū)別對(duì)待。通常NumPy會(huì)盡可能使用單一類(lèi)型的1維數(shù)組(例如,2維數(shù)組a的第j列a[:, j]是1維數(shù)組)。默認(rèn)情況下,一維數(shù)組在2維操作中被視為行向量,因此,將矩陣乘行向量時(shí),使用形狀(n,)或(1,n)的向量結(jié)果一致。有多種方法可以從一維數(shù)組中得到列向量,但并不包括transpose:
使用newaxis更新數(shù)組形狀和索引可以將1維數(shù)組轉(zhuǎn)化為2維列向量:
其中,-1表示在reshape是該維度自動(dòng)決定,方括號(hào)中的None等同于np.newaxis,表示在指定位置添加一個(gè)空軸。
因此,NumPy中共有三種類(lèi)型的向量:1維數(shù)組,2維行向量和2維列向量。以下是兩兩類(lèi)型轉(zhuǎn)換圖:
根據(jù)廣播規(guī)則,一維數(shù)組被隱式解釋為二維行向量,因此通常不必在這兩個(gè)數(shù)組之間進(jìn)行轉(zhuǎn)換,對(duì)應(yīng)圖中陰影化區(qū)域。
嚴(yán)格來(lái)說(shuō),除一維外的所有數(shù)組的大小都是一個(gè)向量(如a.shape == [1,1,1,5,1,1]),因此numpy的輸入類(lèi)型是任意的,但上述三種最為常用??梢允褂胣p.reshape將一維矢量轉(zhuǎn)換為這種形式,使用np.squeeze可將其恢復(fù)。這兩個(gè)功能都通過(guò)view發(fā)揮作用。
矩陣操作
矩陣的拼接有以下兩種方式:
圖示操作僅適用于矩陣堆疊或向量堆疊,而一維數(shù)組和矩陣的混合堆疊只有通過(guò)才可實(shí)現(xiàn),會(huì)導(dǎo)致維度不匹配錯(cuò)誤。因?yàn)榍拔奶岬綄⒁痪S數(shù)組作為行向量,而不是列向量。為此,可以將其轉(zhuǎn)換為行向量,或使用專(zhuān)門(mén)的函數(shù)執(zhí)行此操作:
與stack對(duì)應(yīng)的是split:
矩陣復(fù)制有兩種方式:類(lèi)似粘貼復(fù)制;相當(dāng)于分頁(yè)打印。
delete可以刪除特定的行或列:
相應(yīng)插入操作為insert:
與hstack一樣,append函數(shù)無(wú)法自動(dòng)轉(zhuǎn)置1D數(shù)組,因此需要重新調(diào)整向量形狀或添加維數(shù),或者使用column_stack:
如果僅僅是向數(shù)組的邊界添加常量值,pad函數(shù)是足夠的:
Meshgrids
廣播機(jī)制使得meshgrids變得容易。例如需要下圖所示(但尺寸大得多)的矩陣:
上述兩種方法由于使用了循環(huán),因此都比較慢。MATLAB通過(guò)構(gòu)建meshgrid處理這種問(wèn)題。
meshgrid函數(shù)接受任意一組索引,通過(guò)切片和索引生成完整的索引范圍,然后,函數(shù)根據(jù)I和J實(shí)現(xiàn)運(yùn)算。
在NumPy中有一種更好的方法,無(wú)需在內(nèi)存中存儲(chǔ)整個(gè)I和J矩陣(雖然meshgrid已足夠優(yōu)秀,僅存儲(chǔ)對(duì)原始向量的引用),僅存儲(chǔ)形狀矢量,然后通過(guò)廣播規(guī)實(shí)現(xiàn)其余內(nèi)容的處理:
如果沒(méi)有indexing =’ij‘參數(shù),那么meshgrid將更改參數(shù)的順序,即J,I=np.meshgrid(j,i)——一種用于可視化3D繪圖的“xy”模式(祥見(jiàn)該文檔)。
除了在二維或三維網(wǎng)格上初始化函數(shù)外,網(wǎng)格還可以用于索引數(shù)組:
以上方法在稀疏網(wǎng)格中同樣適用。
矩陣統(tǒng)計(jì)
就像sum函數(shù),numpy提供了矩陣不同軸上的, , mean/median/percentile, 等函數(shù)。
np.amin等同于np.min,這樣做同樣是為了避免from numpy import *可能的歧義。
2維及更高維中的argmin和argmax函數(shù)分別返回最小和最大值的索引,通過(guò)unravel_index函數(shù)可以將其轉(zhuǎn)換為二維坐標(biāo):
和同樣也可作用于特定維度:
矩陣排序
雖然在前文中,axis參數(shù)適用于不同函數(shù),但在二維數(shù)組排序中影響較小:
你通常不需要上述這樣的排序矩陣,axis不是key參數(shù)的替代。但好在NumPy提供了其他功能,這些功能允許按一列或幾列進(jìn)行排序:
1、a[a [:,0] .argsort]表示按第一列對(duì)數(shù)組進(jìn)行排序:
其中,argsort返回排序后的原始數(shù)組的索引數(shù)組。
可以重復(fù)使用該方法,但千萬(wàn)不要搞混:
a = a[a[:,2].argsort]
a = a[a[:,1].argsort(kind=’stable‘)]
a = a[a[:,0].argsort(kind=’stable‘)]
2、函數(shù)lexsort可以像上述這樣對(duì)所有列進(jìn)行排序,但是它總是按行執(zhí)行,并且排序的行是顛倒的(即從下到上),其用法如下:
a[np.lexsort(np.flipud(a[2,5].T))],首先按第2列排序,然后按第5列排序;a[np.lexsort(np.flipud(a.T))],從左到右依次排序各列。
其中,沿上下方向翻轉(zhuǎn)矩陣(沿axis = 0方向,與a [::-1,。。.]等效,其中。。.表示“其他所有維度”),注意區(qū)分它與,fliplr用于1維數(shù)組。
3、sort函數(shù)還有一個(gè)order參數(shù),但該方法極不友好,不推薦學(xué)習(xí)。
4、在pandas中排序也是不錯(cuò)的選擇,因?yàn)樵趐andas中操作位置確定,可讀性好且不易出錯(cuò):
- pd.DataFrame(a).sort_values(by=[2,5]).to_numpy,先按第2列排序,再按第5列排序。
-pd.DataFrame(a).sort_values.to_numpy,按從左到右的順序?qū)λ辛羞M(jìn)行排序。
3、3維及更高維數(shù)組
通過(guò)重塑1維向量或轉(zhuǎn)換嵌套Python列表來(lái)創(chuàng)建3維數(shù)組時(shí),索引分別對(duì)應(yīng)(z,y,x)。索引z是平面編號(hào),(y,x)坐標(biāo)在該平面上移動(dòng):
通過(guò)上述索引順序,可以方便的保留灰度圖像,a[i]表示第i個(gè)圖像。
但這樣的索引順序并不具有廣泛性,例如在處理RGB圖像時(shí),通常使用(y,x,z)順序:首先是兩個(gè)像素坐標(biāo),然后才是顏色坐標(biāo)(中的RGB,中的BGR):
這樣可以方便地定位特定像素,如a[i,j]給出像素(i,j)的RGB元組。
因此,幾何形狀的創(chuàng)建實(shí)際取決于你對(duì)域的約定:
顯然,hstack,vstack或dstack之類(lèi)的NumPy函數(shù)并不一定滿(mǎn)足這些約定,其默認(rèn)的索引順序是(y,x,z),RGB圖像順序如下:
如果數(shù)據(jù)不是這樣的布局,使用concatenate命令可以方便的堆疊圖像,并通過(guò)axis參數(shù)提供索引號(hào):
如果不考慮軸數(shù),可以將數(shù)組轉(zhuǎn)換hstack和相應(yīng)形式:
這種轉(zhuǎn)換非常方便,該過(guò)程只是混合索引的順序重排,并沒(méi)有實(shí)際的復(fù)制操作。
通過(guò)混合索引順序可實(shí)現(xiàn)數(shù)組轉(zhuǎn)置,掌握該方法將加深你對(duì)3維數(shù)據(jù)的了解。根據(jù)確定的軸順序,轉(zhuǎn)置數(shù)組平面的命令有所不同:對(duì)于通用數(shù)組,交換索引1和2,對(duì)于RGB圖像交換0和1:
注意,(a.T)的默認(rèn)軸參數(shù)會(huì)顛倒索引順序,這不同于上述述兩種索引順序。
廣播機(jī)制同樣適用多維數(shù)組,更多詳細(xì)信息可參閱筆記“ ”。
最后介紹(Einstein summation)函數(shù),這將使你在處理多維數(shù)組時(shí)避免很多Python循環(huán),代碼更為簡(jiǎn)潔:
該函數(shù)對(duì)重復(fù)索引的數(shù)組求和。在一般情況下,使用np.tensordot(a,b,axis=1)就可以,但在更復(fù)雜的情況下,einsum速度更快,讀寫(xiě)更容易。
如果你想看看自己的NumPy水平到底如何,可以在GitHub上進(jìn)行練習(xí)——例如。
對(duì)于本文未介紹到的NumPy常用功能,歡迎各位讀者通過(guò)、給我留言,我將進(jìn)一步完善本文!
-
核心
+關(guān)注
關(guān)注
0文章
46瀏覽量
15302 -
矩陣
+關(guān)注
關(guān)注
1文章
437瀏覽量
35711 -
向量
+關(guān)注
關(guān)注
0文章
55瀏覽量
11962
發(fā)布評(píng)論請(qǐng)先 登錄
[教程] Matlab中矩陣、向量及數(shù)組元素的引用方法和討論
如何在labview中實(shí)現(xiàn)二維DFT?
1維數(shù)組轉(zhuǎn)2維數(shù)組
C語(yǔ)言二維數(shù)組的定義和引用
基于雜波協(xié)方差矩陣特征向量分析STAP降維方法

靈活運(yùn)用Python中numpy庫(kù)的矩陣運(yùn)算
C語(yǔ)言程序設(shè)計(jì)教程之二維數(shù)組如何應(yīng)用二維數(shù)組的資料概述
MATLAB入門(mén)教程之MATLAB矩陣和數(shù)組的表示詳細(xì)資料介紹
MATLAB教程之數(shù)組和矩陣的介紹及運(yùn)算說(shuō)明

NumPy 誕生過(guò)去15年后 其核心開(kāi)發(fā)團(tuán)隊(duì)的論文終于在 Nature 上發(fā)表

圖文詳解NumPy看這一篇就夠了

評(píng)論