python數據科學必不可少的庫-numpy

python數據科學必不可少的庫-numpy

numpy對python的意義非凡,本來Python僅僅是一門腳本語言,但是因為numpy的加持,使Python有了在數據科學領域能與matlib、spss等專業軟件一較高下的資本

現在用python搞數據分析或機器學習經常使用的pandas、matplotlib、sklearn等庫,都需要基於numpy構建。

毫不誇張地說,沒有numpy,就沒有python今天在數據分析與機器學習領域的廣泛應用。

NumPy提供了兩種基本的對象:ndarray(N-dimensional array object)和 ufunc(universal function object)。ndarray(下文統一稱之為數組)是存儲單一數據類型的多維數組,而ufunc則是能夠對數組進行處理的函數。

1,ndarray對象

我們可以通過給array函數傳遞Python的序列對象創建數組,如果傳遞的是多層嵌套的序列,將創建多維數組

python數據科學必不可少的庫-numpy

python數據科學必不可少的庫-numpy

python數據科學必不可少的庫-numpy

數組的大小可以通過其shape屬性獲得

python數據科學必不可少的庫-numpy

可以通過修改數組的shape屬性,在保持數組元素個數不變的情況下,改變數組每個軸的長度。下面的例子將數組c的shape改為(4,3),注意從(3,4)改為(4,3)並不是對數組進行轉置,而只是改變每個軸的大小,數組元素在內存中的位置並沒有改變

當某個軸的元素為-1時,將根據數組元素的個數自動計算此軸的長度,因此下面的程序將數組c的shape改為了(2,6)

python數據科學必不可少的庫-numpy

使用數組的reshape方法,可以創建一個改變了尺寸的新數組,原數組的shape保持不變

python數據科學必不可少的庫-numpy

數組的元素類型可以通過dtype屬性獲得

python數據科學必不可少的庫-numpy

先創建一個Python序列,然後通過array函數將其轉換為數組,這樣做顯然效率不高。因此NumPy提供了很多專門用來創建數組的函數

arange函數類似於python的range函數,通過指定開始值、終值和步長來創建一維數組,注意數組不包括終值:

python數據科學必不可少的庫-numpy

linspace函數通過指定開始值、終值和元素個數來創建一維等差的數組,可以通過endpoint關鍵字指定是否包括終值,缺省設置是包括終值

python數據科學必不可少的庫-numpy

logspace函數和linspace類似,不過它創建等比數列,下面的例子產生1(10^0)到100(10^2)、有20個元素的等比數列:

python數據科學必不可少的庫-numpy

存取元素

數組元素的存取方法和Python的標準方法相同

python數據科學必不可少的庫-numpy

多維數組的存取和一維數組類似,因為多維數組有多個軸,因此它的下標需要用多個值來表示,NumPy採用組元(tuple)作為數組的下標

python數據科學必不可少的庫-numpy

python數據科學必不可少的庫-numpy

2,ufunc

ufunc是universal function的縮寫,它是一種能對數組的每個元素進行操作的函數。NumPy內置的許多ufunc函數都是在C語言級別實現的,因此它們的計算速度非常快。

python數據科學必不可少的庫-numpy

廣播

當我們使用ufunc函數對兩個數組進行計算時,ufunc函數會對這兩個數組的對應元素進行計算,因此它要求這兩個數組有相同的大小(shape相同)。如果兩個數組的shape不同的話,會進行如下的廣播(broadcasting)處理:

1. 讓所有輸入數組都向其中shape最長的數組看齊,shape中不足的部分都通過在前面加1補齊

2. 輸出數組的shape是輸入數組shape的各個軸上的最大值

3. 如果輸入數組的某個軸和輸出數組的對應軸的長度相同或者其長度為1時,這個數組能夠用來計算,否則出錯

4. 當輸入數組的某個軸的長度為1時,沿著此軸運算時都用此軸上的第一組值

python數據科學必不可少的庫-numpy

ufunc函數本身還有些方法,這些方法只對兩個輸入一個輸出的ufunc函數有效,其它的ufunc對象調用這些方法時會拋出ValueError異常。

reduce 方法和Python的reduce函數類似,它沿著axis軸對array進行操作,相當於將運算符插入到沿axis軸的所有子數組或者元素當中。

.reduce (array=, axis=0, dtype=None)

例如:

>>> np.add.reduce([1,2,3]) # 1 + 2 + 3

6

>>> np.add.reduce([[1,2,3],[4,5,6]], axis=1) # 1,4 + 2,5 + 3,6

array([ 6, 15])

accumulate 方法和reduce方法類似,只是它返回的數組和輸入的數組的shape相同,保存所有的中間計算結果:

>>> np.add.accumulate([1,2,3])

array([1, 3, 6])

>>> np.add.accumulate([[1,2,3],[4,5,6]], axis=1)

array([[ 1, 3, 6],

[ 4, 9, 15]])

reduceat 方法計算多組reduce的結果,通過indices參數指定一系列reduce的起始和終了位置。reduceat的計算有些特別,讓我們通過一個例子來解釋一下:

>>> a = np.array([1,2,3,4])

>>> result = np.add.reduceat(a,indices=[0,1,0,2,0,3,0])

>>> result

array([ 1, 2, 3, 3, 6, 4, 10])

對於indices中的每個元素都會調用reduce函數計算出一個值來,因此最終計算結果的長度和indices的長度相同。結果result數組中除最後一個元素之外,都按照如下計算得出:

if indices[i] < indices[i+1]:

result[i] = np.reduce(a[indices[i]:indices[i+1]])

else:

result[i] = a[indices[i]

而最後一個元素如下計算:

np.reduce(a[indices[-1]:])

因此上面例子中,結果的每個元素如下計算而得:

1 : a[0] = 1

2 : a[1] = 2

3 : a[0] + a[1] = 1 + 2

3 : a[2] = 3

6 : a[0] + a[1] + a[2] = 1 + 2 + 3 = 6

4 : a[3] = 4

10: a[0] + a[1] + a[2] + a[4] = 1+2+3+4 = 10

可以看出result[::2]和a相等,而result[1::2]和np.add.accumulate(a)相等。

outer 方法,.outer(a,b)方法的計算等同於如下程序:

>>> a.shape += (1,)*b.ndim

>>> (a,b)

>>> a = a.squeeze()

其中squeeze的功能是剔除數組a中長度為1的軸。如果你看不太明白這個等同程序的話,讓我們來看一個例子:

>>> np.multiply.outer([1,2,3,4,5],[2,3,4])

array([[ 2, 3, 4],

[ 4, 6, 8],

[ 6, 9, 12],

[ 8, 12, 16],

[10, 15, 20]])

可以看出通過outer方法計算的結果是如下的乘法表:

# 2, 3, 4

# 1

# 2

# 3

# 4

# 5

如果將這兩個數組按照等同程序一步一步的計算的話,就會發現乘法表最終是通過廣播的方式計算出來的。

2.3矩陣運算

NumPy和Matlab不一樣,對於多維數組的運算,缺省情況下並不使用矩陣運算,如果你希望對數組進行矩陣運算的話,可以調用相應的函數。

matrix對象

numpy庫提供了matrix類,使用matrix類創建的是矩陣對象,它們的加減乘除運算缺省採用矩陣方式計算,因此用法和matlab十分類似。但是由於NumPy中同時存在ndarray和matrix對象,因此用戶很容易將兩者弄混。這有違Python的"顯式優於隱式"的原則,因此並不推薦在較複雜的程序中使用matrix。下面是使用matrix的一個例子:

>>> a = np.matrix([[1,2,3],[5,5,6],[7,9,9]])>>> a*a**-1matrix([[ 1.00000000e+00, 1.66533454e-16, -8.32667268e-17], [ -2.77555756e-16, 1.00000000e+00, -2.77555756e-17], [ 1.66533454e-16, 5.55111512e-17, 1.00000000e+00]])

因為a是用matrix創建的矩陣對象,因此乘法和冪運算符都變成了矩陣運算,於是上面計算的是矩陣a和其逆矩陣的乘積,結果是一個單位矩陣。

矩陣的乘積可以使用dot函數進行計算。對於二維數組,它計算的是矩陣乘積,對於一維數組,它計算的是其點積。當需要將一維數組當作列矢量或者行矢量進行矩陣運算時,推薦先使用reshape函數將一維數組轉換為二維數組:

>>> a = array([1, 2, 3])>>> a.reshape((-1,1))array([[1], [2], [3]])>>> a.reshape((1,-1))array([[1, 2, 3]])

除了dot計算乘積之外,NumPy還提供了inner和outer等多種計算乘積的函數。這些函數計算乘積的方式不同,尤其是當對於多維數組的時候,更容易搞混。

· dot : 對於兩個一維的數組,計算的是這兩個數組對應下標元素的乘積和(數學上稱之為內積);對於二維數組,計算的是兩個數組的矩陣乘積;對於多維數組,它的通用計算公式如下,即結果數組中的每個元素都是:數組a的最後一維上的所有元素與數組b的倒數第二位上的所有元素的乘積和:

· dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])

下面以兩個3為數組的乘積演示一下dot乘積的計算結果:

首先創建兩個3維數組,這兩個數組的最後兩維滿足矩陣乘積的條件:

>>> a = np.arange(12).reshape(2,3,2)>>> b = np.arange(12,24).reshape(2,2,3)>>> c = np.dot(a,b)

dot乘積的結果c可以看作是數組a,b的多個子矩陣的乘積:

>>> np.alltrue( c[0,:,0,:] == np.dot(a[0],b[0]) )True>>> np.alltrue( c[1,:,0,:] == np.dot(a[1],b[0]) )True>>> np.alltrue( c[0,:,1,:] == np.dot(a[0],b[1]) )True>>> np.alltrue( c[1,:,1,:] == np.dot(a[1],b[1]) )True

· inner : 和dot乘積一樣,對於兩個一維數組,計算的是這兩個數組對應下標元素的乘積和;對於多維數組,它計算的結果數組中的每個元素都是:數組a和b的最後一維的內積,因此數組a和b的最後一維的長度必須相同:

· inner(a, b)[i,j,k,m] = sum(a[i,j,:]*b[k,m,:])

下面是inner乘積的演示:

>>> a = np.arange(12).reshape(2,3,2)>>> b = np.arange(12,24).reshape(2,3,2)>>> c = np.inner(a,b)>>> c.shape(2, 3, 2, 3)>>> c[0,0,0,0] == np.inner(a[0,0],b[0,0])True>>> c[0,1,1,0] == np.inner(a[0,1],b[1,0])True>>> c[1,2,1,2] == np.inner(a[1,2],b[1,2])True

· outer : 只按照一維數組進行計算,如果傳入參數是多維數組,則先將此數組展平為一維數組之後再進行運算。outer乘積計算的列向量和行向量的矩陣乘積:

· >>> np.outer([1,2,3],[4,5,6,7])· array([[ 4, 5, 6, 7],· [ 8, 10, 12, 14],· [12, 15, 18, 21]])

矩陣中更高級的一些運算可以在NumPy的線性代數子庫linalg中找到。例如inv函數計算逆矩陣,solve函數可以求解多元一次方程組。下面是solve函數的一個例子:

>>> a = np.random.rand(10,10)>>> b = np.random.rand(10)>>> x = np.linalg.solve(a,b)>>> np.sum(np.abs(np.dot(a,x) - b))3.1433189384699745e-15

solve函數有兩個參數a和b。a是一個N*N的二維數組,而b是一個長度為N的一維數組,solve函數找到一個長度為N的一維數組x,使得a和x的矩陣乘積正好等於b,數組x就是多元一次方程組的解。


分享到:


相關文章: