【Python】 Numpy極簡尋路

【Numpy】python

  先感嘆下最近挖坑愈來愈多了。。算法

  最近想蚍蜉撼樹地挑戰下ML甚至DL。然而我也知道對於我這種半路出家,大學數學也只學了兩個學期,只學了點最基本的高數還都忘光了的渣滓來講,難度估計有點大。。總之盡力而爲吧。在正式接觸ML的算法以前,Numpy是一個必須知道的Python庫。其中有不少關於線代的類和方法能夠直接用。數組

  固然Numpy不是內建的庫,可是pip install numpy一下也很簡單。app

  ■  方法羅列dom

  我也不知道怎麼開始寫好,按書上的教程,羅列下提到的方法吧。。書上代碼一個大前提是from numpy import *。可是有點Python經驗的人都知道,import *不是一個很好的引入辦法。因此我仍是把模塊名給老實寫出來。yii

  numpy.random  這仍是個小模塊,相似python內建的random模塊,裏面涵蓋了不少用於隨機生成一些數據的方法如random,randint,choice等等。ide

  numpy.random.rand(a,b)  這個rand方法是不太熟悉的,其做用是生成一個a * b的二維數組,數組中的每個元素都是隨機出來的。雖然兩個循環或者 嵌套列表表達式也能作這個事,不過一個方法就搞定更快。函數

  numpy.mat  此方法就是將某個二維數組轉化成一個矩陣。因爲矩陣的類Matrix類的__str__方法是返回二維數組的形式,因此print的時候看起來和rand出來的東西沒差異,可是其實是個矩陣對象。spa

  矩陣對象matrix的I(大寫的i)屬性是其逆矩陣。經過*符號將二者相乘便可獲得單位矩陣:code

>>> nest_list = numpy.random.rand(3,3)
>>> matrix = numpy.mat(nest_list)
>>> matrix
matrix([[ 0.31934803,  0.65214401,  0.78380241],
        [ 0.00338375,  0.64812103,  0.19773746],
        [ 0.08785176,  0.04199491,  0.13058765]])
>>> invMat = matrix.I
>>> invMat
matrix([[ -8.38826956,   5.74139174,  41.65369116],
        [ -1.86042236,   2.98414596,   6.64784212],
        [  6.24142078,  -4.82212736, -22.50232256]])
>>> invMat * matrix
matrix([[  1.00000000e+00,  -1.55431223e-15,  -8.88178420e-16],
        [  0.00000000e+00,   1.00000000e+00,  -2.22044605e-16],
        [  0.00000000e+00,   6.66133815e-16,   1.00000000e+00]])

 

  相乘以後獲得的單位矩陣中,除了對角線外其他部分不是0的緣由是由於處理浮點數的偏差。

  numpy.eye(n)  eye方法能夠直接生成n階的單位矩陣。好比上面那個矩陣直接減去eye(3)就會變成空矩陣了。

  numpy.linspace(a,b,n)  abn三個參數都是實數且n是整數,這個函數返回一個array對象,其內容是將[a,b]範圍按照n-1等分,而後各個等分的節點上的數(統一爲float型)填充進array中。因爲兩端都閉合,因此返回列表的長度是n。好比linspace(0,1,10)返回的是[0., 0.1111111, 0.22222 ..... 0.999999, 1.]

  numpy.arange(a,b,s)  和linspace很像,只不過這個函數,是從a開始(包括a)逐漸一個個加上s,並把每加上一個s以後的值充入array,直到值大於等於b時中止。須要注意區間右端開,即若是a + k*s以後恰好等於b,那麼b是不被加入這個array的。

 

■  array類

  numpy.array是整個numpy中最爲重要的類型之一。相比於Python中自帶的數組類型(列表),numpy中的array作了不少利於數學計算的封裝。

  好比對於 array對象a 來講,有以下屬性可使用:

a.T             a.choose        a.data          a.flatten       a.nbytes        a.repeat        a.sort          a.tostring
a.all           a.clip          a.diagonal      a.getfield      a.ndim          a.reshape       a.squeeze       a.trace
a.any           a.compress      a.dot           a.imag          a.newbyteorder  a.resize        a.std           a.transpose
a.argmax        a.conj          a.dtype         a.item          a.nonzero       a.round         a.strides       a.var
a.argmin        a.conjugate     a.dump          a.itemset       a.prod          a.searchsorted  a.sum           a.view
a.argsort       a.copy          a.dumps         a.itemsize      a.ptp           a.setfield      a.swapaxes
a.astype        a.ctypes        a.fill          a.max           a.put           a.setflags      a.take
a.base          a.cumprod       a.flags         a.mean          a.ravel         a.shape         a.tofile
a.byteswap      a.cumsum        a.flat          a.min           a.real          a.size          a.tolist        

  首先須要說明的是,這裏的數組是廣義上的數組。便可以不止一維。換句話說,嵌套多層的數組也就是n維數組也可使用這裏的方法。

  其次,array對象和多層嵌套的列表是不一樣的。好比對於array對象的加減乘除等操做就不一樣。對於普通列表[1,2,3]和array([1,2,3])而言。前者若是令 ls * 2,獲得的是[1,2,3,1,2,3],然後者獲得的是[2,4,6]。更牛的是後者還能夠作2 * ls + 1造成[3,5,7]。也就是說,默認的加減乘除對於array對象而言實際上是對於數組中全部成員數的直接操做求值。

  關於數組的維度: 對於一個最小組成單位統一(一般就是整數,這個和上面屬性中的dtype有關),從代碼來講只要看數組的表達式從最開頭開始,到第一個最小組成單位爲止,之間有幾個中括號,那就是幾維數組。人類大腦比較容易接受的有一維數組[1,2,3],或者二維數組[[0,1,2],[1,2,3]]。前者不用說,後者的話能夠理解成一些座標系內點的集合(不過也就到三維座標系爲止,以上維度的很難想象)。複雜的高維數組,簡單來講能夠理解成組成數組的元素是小數組,而小數組中又有小小數組這樣子。

  對於高維數組的取值,固然能夠用a[x][y]這樣的方式來取值,可是中括號接中括號很很差看。array類實現了中括號中多參數的辦法來取值。好比a[x,y]就能夠了。甚至能夠有a[x,:]這種切片也放在這裏。這些還都是相似於Python列表基於行的取值。若是將切片放在第一個參數甚至能夠作到整個數組基於列的取值,好比a[:,1],就能夠取到全部行的第一列的數據(固然舉例時想成二維數組便可。)

  簡單說明一下其中的幾個(方法後面會帶上括號,沒有括號的就說明是屬性)

  a.all()和a.any()是判斷數組的黑白狀況。所謂黑白,就是將整個數組flatten成一個一維的數組,而後查看組成其的基本元素是否全是真或者至少有一個真(對於int型,真就是指!=0)。若是全是真,a.all()返回True,不然返回False。若是至少有一個是真a.any()返回True,不然False

  a.max()和min()就是把a給flatten以後獲取最大、最小值。相對應的argmax()和argmin()返回的是最大值和最小值所在位置的下標。

  a.size會返回數組中最小組成單位的總個數。注意a.size和len(a)在高於一維的狀況下明顯是不等的。len(a)計算獲得的是第一維上獲得的長度。

  a.dtype會返回組成此數組的最小單位的基本類型。若是全是數字則返回int(64),若是是相似於[[1,2],3]這樣不規則的混合類型,則返回object

  a.ndim  返回數組的維數。numpy中數組的維數並不要求必定要有具體內容。好比array([])的ndim就是1,而array([[]])就是2

  a.shape  返回一個元組,內容是數組在各個維度上的長度。這個比較拗口了,對於一個規則的能夠造成矩陣的數組,其特定一個維度上各個元素的長度應該是相同的。因此咱們能夠獲得一個統一的shape。好比對於下面這幾個數組的shape返回值,體會一下:

[1,2,3]
# (3,)

[[0,1,2],[1,2,3]]
# (2,3)
# 從最外面往裏面走,第一層(第一維)長度是2,由兩個元素組成。剛好這倆元素都是數組,說明有第二維,繼續往裏走
# 第二維的長度是3,因此最終數組在各個維度上的長度用shape體現出來就是(2,3)

[
  [
    [1,2,3],
    [4,5,6]
  ],
  [
    [7,8,9],
    [10,11,12]
  ]
]
# 同上理,這個三維數組的shape是(2,2,3)

 

  能夠知道,a.shape這個元組的長度等於a.ndim,各個元素之積等於a.size。

  a.reshape(*args)  能夠向reshape方法傳遞一些數字,只要這些數字的積是a.size,那麼就返回一個shape爲指定那些數字的一個數組。a能夠不是一個flatten的數組,同時這個方法是返回我說的那樣的一個新數組而不是在a自己作出操做。

  a.flatten()  將高維數組降成線性的。一樣是返回一個新數組而不是在a自己操做。

  a.sum(axis)  首先要知道什麼是數組間的求和。不一樣於[1,2] + [3,4]獲得的是[1,2,3,4],array([1,2]) + array([3,4])獲得的是array([4,6])。順便,array([1,2]) + array([3,]) = array([4,5])。如今重點關注前面這種規則的相加形式。axis參數能夠是一個數字,它指出了咱們要合併數組的第幾維,axis具體數值對應的是shape元組的下標。例如對於數組a = array([[1,2],[3,4],[5,6]]),有a.shape == (3,2),因此axis能夠是0或者1,分別對應a的第一維和第二維。根據shape,第一維的長度是3,第二維的長度是2。在作了sum操做以後,返回的內容應該是axis指定的那個維度被合併以後的狀況。好比指定axis=0時,最終應該返回一個shape是(2,)的數組;若axis=1時,返回一個最終shape爲(3,)的數組。

  那麼具體怎麼操做呢?之前者的狀況爲例,合併第一維,指的是將第一維上各元素相加。第一維上的元素分別是[1,2]和[3,4]和[5,6]這三個數組。根據數組相加規則,最終獲得的就是[1+3+5, 2+4+6]即[9,12]。這個數組恰好shape是(2,)符合咱們的預期。同理,後者是要合併第二維,因此要在第二維的層面上看,第二維的層面就是[1,2]中的1和2,以及[3,4]中的3和4……。將它們分別相加,獲得的是[1+2,3+4,5+6]即[3,7,11],這個的shape也恰好是(3,)。

  以上是axis參數爲單純一個數字的狀況,其實還能夠以元組的形式同時指定多個維度要合併。好比這個例子中指定axis=(0,1),那麼最終數組被合併成21這一個數字。當不指定axis參數時默認就是合併成零維數組,即一個數字。換句話說,a.sum()其實就是a.sum(tuple(range(len(a.shape))))。

  tile(A,reps)  tile是numpy.tile,一個獨立的方法。其第一個參數是一個若干維的數組(包括零維),第二個是一個合法表示的shape量。正常狀況下,tile的操做是將A的第n維重複reps[n-1]次。若是當前維度尚未到達最底層,那麼就是數組層面的重複;若是已經到達最底層,那麼就是作了相似於lst.append(*lst)這樣的操做。例子:

a = array([[1,2],[3,4],[5,6]])
tile(a,(2,1))
'''
獲得結果
array([[1, 2],
       [3, 4],
       [5, 6],
       [1, 2],
       [3, 4],
       [5, 6]])
reps=(2,1)之意爲第一維上*2即再重複一遍,第一維原數據是三個小數組組成的數組,沒有到達底層,因此數組層面上重複,獲得結果第一維的長度從原先3變成了6
第二維reps是1,即*1,即不變
'''
tile(a,(1,2))
'''
array([[1, 2, 1, 2],
       [3, 4, 3, 4],
       [5, 6, 5, 6]])
若是改爲第二維*2,因爲第二維已是底層,因此原來元素直接重複一遍,而不是變成
[[1,2],[1,2]],[[3,4],[3,4]]... 這樣維度上升了!
'''

   上面的狀況,reps的長度恰好和A的維數同樣。若是不同呢。設reps長度爲D,a的維數是a.ndim。若是D>a.ndim,就在a外面升維,升的每一個維度長度都是1。升到A的維數和D相等再作tile操做;

  若D<a.ndim,就在D前面擴充1,好比(2,2)將被擴充爲(1,1,1...2,2)使得D等於a.ndim,再作tile操做。換句話說,D不夠長時,指出的須要進行重複操做的維度默認從最底層開始算。

  zeros  zeros也是numpy.zeros,其參數能夠是一個合法的shape量,用來生成指定shape的array,其中最小單位元素由數字0填充。默認這個0是float即0.0,能夠dtype參數指定int等改變其類型。

  a.argsort()  經過一個array對象a調用,使用後返回另外一個array,內容是按照從小到大順序排列a後,各個元素在原來a中的下標。好比array([4,1,3,2]).argsort()返回的是array([1,3,2,0])

  may_share_memory(a,b)  在Python中原生的切片功能中,默認切片後會建立出一個新的對象來保存數據。可是array對象實際上是對一個序列對象的部分引用,所以有可能出現array切片後不建立一個新對象。好比a = array([1,2,3]),b = a[1:],b[1] = 5。這裏把a切片出一部分賦值給了b,看似b和a就獨立開來了。可是在對b作了一些改變以後a也會隨之被改變。即a如今是array([1,2,5])。而may_share_memory就是numpy留出的一個判斷兩個array中是否有同指向引用的接口。它返回一個True或者False。

  a.copy()  copy方法就是用來解決上面說的這個問題的。固然你也能夠用Python自帶的copy模塊的deepcopy方法。

  numpy.dot(a,b)  剛纔也說了,array對象的乘法是各個對應位置分別相乘,而不是矩陣乘法。若是要矩陣乘法,一個辦法是轉化array爲mat對象後相乘。另外一個就是使用dot函數相乘兩個array。

 

  ● 總結一下

  自己運算的特殊性:array類對象很好地直接和運算符號結合。如兩個shape同樣的array類對象a和b,a+b,a-b,a*b,a/b都是直接將各個對應位置的元素進行相應的計算。a % 2 == 0返回的是一個所有都是True,False,可是shape和a同樣的array。取True仍是False就是看相關位置的元素%2是否是0了。

  進行array的乘除時,若是二者的shape不匹配,也不必定就是不能計算。numpy中會有一種「廣播機制」(boardcast),若是在一個或多個維度方向上重複運算對象中比較小的array,使得比較小的array可以恰好覆蓋掉比較大的array,一個元素都很少很多,此時numpy就會默認這樣作。好比a = array([[1,1],[2,2],[3,3]])加上了b = array([1,1]),那麼得出結果應該是array([[2,2],[3,3],[4,4]])

  切片:array的切片比Python自帶的序列切片更加高級。除了a[1:],a[:2],a[::3]等等切片方式以外,還支持

  如a[2,3:5]之類的tuple做爲判別式的切片(全部條件都寫在一箇中括號中,不用再去寫兩個中括號了)

  如a[a>=10]這樣的「邏輯切片」,這類須要注意中括號中的a必須和數組名保持一致。

  如a[[2,4,5]]這樣的離散式地選擇一些值。

  關於形態:a.shape, a.size, a.ndim  這些是屬性。a.reshape(*args),  a.flatten() 這些是方法。

  考察數組中內容:a.all(),a.any()    a.max(), a.argmax(axis), a.min(), a.argmin(axis)    a.sum(axis)    a.dtype    a.argsort()

  numpy直屬函數:tile(element, axis)    zeros/ones()

  numpy中集成的一些相似其餘模塊的函數: sqrt/cos/sin/exp()    arange()    a.copy()   

  numpy.random中也有不少和random中相似的方法。比較經常使用的有numpy.random.random_intergers(a,b, (c,d))用來生成一個shape爲(c,d)的array。每一個元素都從[a,b]範圍中隨機選一個值。

  

 

  ●  關於axis參數與降維

  上面提到過array調用sum方法的時候能夠傳入axis參數。實際上在其餘的一些方法中好比min,max中,axis參數也有很普遍的運用。下面對axis參數的意義再作一次解釋說明。

  能夠感受獲得,axis的降維操做應該是一種成體系的數學上的操做,不過我還沒學到過… 只能從經驗主義的角度來總結一下axis 的用法。

  首先咱們能夠經過Python代碼中比較司空見慣的高維數組的寫法中肯定這個數組的各個維的長度。具體的,咱們要肯定每一維的元素個數有幾個。以下圖所示:

 

  很明顯,圖中的這個array是一個三維的數組,而每一個維度的長度就是各個維度的一個單位元素中有多少個次維度元素,便可以畫多少個箭頭。第一維這個列表中包含兩個子列表,第二維也是,第三維單位元素則包含三個數字,因此這個數組a的shape是(2,2,3)。

  而後當咱們調用sum,或者max,min之類的方法,指定axis參數時,axis參數對應的是shape的一個(或者若干個,這裏先以一個爲例)下標。它最直接的含義是,這個方法返回的array的shape,應該是原array的shape去掉相應下標後的值。如上面的a,若是調用了sum(axis=1),那麼方法返回的應該是一個shape是(2,3)的array;若是是sum(axis=2),那麼返回的應該是一個shape是(2,2)的array。

  僅僅知道shape是不夠的,那麼怎麼構造這樣一個降維後的array,知道其具體的值呢?一個直觀的方法是,哪一個維度被指定爲axis了,就是指這個維度對應的那些箭頭在一個「父元素」內只能存在一個了。好比sum(axis=1),此時第二維被指定爲axis。第二維元素對應的是綠色的箭頭,目前有兩個綠色的箭頭。那麼怎麼樣才能只保留一個綠色的箭頭呢,就是將兩個綠色的箭頭指向的元素上下相加(array互相相加至關於各個元素互相相加),所以咱們獲得的是一個(2,3)的二維數組是這樣的:[[1,3,5],[4,2,2]]。

  相似的sum(axis=2)是將紅色的箭頭通通合併,因此相加是橫向進行的。

  若是換個方法,max和min中指定axis,其實要義也是隻保留一個同色箭頭,可是這裏保留的具體方法就不是加和全部同色箭頭指向的元素,而是找出同色箭頭指向元素中的最大/最小值了。如箭頭指向的元素不是一個數字值,而是一個數組甚至高維數組呢?也很簡單,就是比較這些元素相同位置的數字值,只選取其中的最大/最小值便可。好比max(axis=0)時,兩個藍色箭頭指向的元素都是個二維數組,那麼就比較二維數組中各個位置的值並取其大者。最終呈現出來的東西,就是[[3,1,4],[1,2,1]]

  

■  numpy中提供的一些多項式操做以及和線性代數相關的一些內容

  np.poly1d(list)  能夠經過numpy隱性地構造一個多項式。好比np.poly1d([1,-4,3])就表明了x^2 - 4x + 3這個多項式。若是令p = np.poly1d([1,-4,3]),能夠進行以下運算
  p(0)  代入x = 0時多項式的值

  p.roots  求出多項式等於0時這個方程的根

  p.order  多項式的階數

  p.coeffs  多項式的係數,即上面給出的list

  np.polyfit(x,y,degree)  這個函數能夠用於將(x, y)這組數據組成的點擬合成一個多項式,多項式的最高次數能夠經過degree指定。而後這個函數返回的東西是一個係數的列表,將其放在poly1d()裏面就能夠構造出這個多項式對象。而後調用p(n)就能夠大概估計x爲n的時候y的值啦。*x不能太離譜,就比如ML同樣,若是用於訓練的數據是0,1範圍內,可是n忽然給了個10,那計算出來的確定不是很準的。

 

 https://www.yiibai.com/numpy

相關文章
相關標籤/搜索