Python資料分析(一)--numpy全知全會

weixin_33912246發表於2018-12-15

這是python在資料分析和機器學習系列文章的第一篇。
本文全面闡述numpy的方方面面,以供參考。
numpy不僅是資料分析的基礎包,也是機器學習進行科學計算的基礎。

一、寫在前面

匯入包

import numpy
#通常的做法是指定一個別名
#下面例子任何地方使用到的np若無特殊說明,一律都是指numpy
import numpy as np 
#當然也可以這樣匯入全部,但是強烈建議不要這樣做
from numpy import *

基礎屬性

numpy的主要物件是多維陣列,既然是陣列,就必要要有相同的資料型別,同時也可以進行各種計算和操作。關於基礎概念,下面以一個例子來詳細說明

例子

>>> import numpy as np
>>> n1 = np.array([1,2,3,4],np.int)  #一維陣列
>>> n1
array([1, 2, 3, 4])
>>> type(n1)
<class 'numpy.ndarray'>
>>> n1.shape  #形狀,1行4列
(4,)
>>> n1.ndim  #維度,這是一維
1
>>> n1.size  #元素個數
4
>>> n1.dtype  #元素的資料型別
dtype('int32')
>>> n1.itemsize  #每個元素的位元組大小
4
#為了更好說明以上的屬性,以一個多維陣列為例就很明顯了
>>> n2 = np.arange(24).reshape(3,2,4)    #三維陣列,arange函式生成的元素值為0到23
>>> n2
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]],

       [[16, 17, 18, 19],
        [20, 21, 22, 23]]])
>>> n2.shape 
(3, 2, 4)
>>> n2.ndim
3
>>> n2.dtype
dtype('int32')
>>> n2.size
24

array的建立

通過上面的例子,numpy陣列可由常規的List進行建立。此外,numpy也提供了建立特定陣列的函式。

例子

>>> n3 = np.zeros([2,3])   #zeros建立全零陣列,可指定長度
>>> n3
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])
>>> n301 = np.ones([2,3]) #同zeros,ones建立全為1的陣列
>>> n301
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])    
>>> n302 = np.empty((3,3))  #empty建立的陣列元素值隨機分配,與記憶體狀態有關
>>> n302
array([[  1.19563886e-321,   0.00000000e+000,   0.00000000e+000],
       [  0.00000000e+000,   0.00000000e+000,   1.08694442e-321],
       [  0.00000000e+000,   0.00000000e+000,   0.00000000e+000]])
#此外,還有zeros_like、ones_like、empty_like函式,引數為一個陣列,
#建立出形狀與給定陣列相同的全零、全一或隨機值的陣列。
#以zeros_like為例
>>> n4 = np.arange(12).reshape(3,4)
>>> n4
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> np.zeros_like(n4)   #形狀與n4相同的全零陣列
array([[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0]])
>>> a = np.arange(1,3,0.5)  #指定範圍和步長,生成一維陣列
>>> a
array([ 1. ,  1.5,  2. ,  2.5])       
>>> np.linspace(1,3,5)  #指定範圍和元素個數
array([ 1. ,  1.5,  2. ,  2.5,  3. ])  
#隨機陣列,random中還有諸如randn、random之類的隨機函式可生成陣列  
>>> np.random.rand(3,2)  
array([[ 0.27133972,  0.73467037],
       [ 0.41100736,  0.75078034],
       [ 0.12516107,  0.98181898]])            

array的操作

  • 一維陣列切片比較好理解,對於多維甚至高維陣列來說,切片就比較抽象,要指定哪一個軸來切。所以,弄清楚高維陣列的軸很重要。
  • 將一個陣列切分為幾個小陣列。
  • 很多時候需要對陣列進行維度變換,比如將一維陣列變成三維陣列等,或者將高維陣列拍扁成低維陣列。
  • 根據不同的軸將陣列進行堆積操作。
  • 完全無拷貝、淺拷貝與深拷貝等。下面用一個例子詳細瞭解下。

例子

# 首先看看陣列的切片,以三維陣列為例
>>> n5 = np.arange(24).reshape(3,2,4)
>>> n5
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]],

       [[16, 17, 18, 19],
        [20, 21, 22, 23]]])
#獲取某個特定的值,可以這樣
>>> n5[1,1,1]
13
>>> n5[n5%2==0]    #選出陣列中的偶數
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18, 20, 22])
>>> i = [0,0,1,1,1]
>>> j = [1,1,0,0,0]
>>> k = [1,1,0,0,1]
>>> n5[i,j,k]   #i,j,k需要有相同的長度
array([5, 5, 8, 8, 9])
>>> n5[:,:,:]      #全切片,只要理解了這種切分方式,就很好理解取得各個維度的資料該怎麼寫了
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]],

       [[16, 17, 18, 19],
        [20, 21, 22, 23]]])
>>> n5[:,:,1:2]     #篩選出第三個維度的第二個資料。注意,第一二維是全部資料
array([[[ 1],
        [ 5]],

       [[ 9],
        [13]],

       [[17],
        [21]]])        
#陣列的維度變換,對於上面的三維陣列。
>>>n5.reshape(4,6)  #轉換成四行六列的二維陣列,建立一個副本,n5本身沒有變
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])    
>>> n5.shape = 4,6     #改變了陣列本身的形狀
>>> n5
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23]])          
>>> n5.shape =3,2,-1     
>>> n5.shape
(3, 2, 4)  
#把一個陣列切分為多個較小的陣列
>>> n6
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> np.hsplit(n6,2)  #相當於把陣列切成水平方向的兩個小陣列
[array([[0, 1],
       [4, 5],
       [8, 9]]), array([[ 2,  3],
       [ 6,  7],
       [10, 11]])]  
>>> np.hsplit(n6,(1,3))   #指定列前後切
[array([[0],
       [4],
       [8]]), array([[ 1,  2],
       [ 5,  6],
       [ 9, 10]]), array([[ 3],
       [ 7],
       [11]])]       
>>> np.vsplit(n6,3)
[array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]]), array([[ 8,  9, 10, 11]])]       
#陣列堆積操作。對如下兩個2*3的二維陣列
>>> a = np.arange(6).reshape(2,3)
>>> b = np.linspace(6,12,6).reshape(2,3)
>>> a
array([[0, 1, 2],
       [3, 4, 5]])
>>> b
array([[  6. ,   7.2,   8.4],
       [  9.6,  10.8,  12. ]])
>>> np.vstack((a,b))   #按照行來堆積,可理解為垂直排
array([[  0. ,   1. ,   2. ],
       [  3. ,   4. ,   5. ],
       [  6. ,   7.2,   8.4],
       [  9.6,  10.8,  12. ]])
>>> np.hstack((a,b))  #按照列來堆積,可理解為水平方向並排一起
array([[  0. ,   1. ,   2. ,   6. ,   7.2,   8.4],
       [  3. ,   4. ,   5. ,   9.6,  10.8,  12. ]])   
#關於陣列的拷貝
>>> c = np.arange(2,9)
#d和c是完全相同的陣列,儲存位置一樣
>>> d=c    
>>> d is c
True 
#e是c的淺拷貝,有相同的元素,一個修改另一個也被修改,但儲存的位置不一樣  
>>> e = c.view()
>>> e
array([2, 3, 4, 5, 6, 7, 8])
>>> e[0] = 333
>>> c
array([333,   3,   4,   5,   6,   7,   8])
>>> e
array([333,   3,   4,   5,   6,   7,   8])
>>> e is c
False 
#f是c的深拷貝,修改值相互不影響
>>> f = c.copy()
>>> f
array([333,   3,   4,   5,   6,   7,   8])
>>> f[0]=2
>>> c
array([333,   3,   4,   5,   6,   7,   8]) 

二、向量和矩陣

numpy在向量和矩陣的運算中扮演著十分重要的角色,不僅提供許多常用的數學和統計函式,並且計算速度也很不錯。

基礎運算

numpy陣列的算術運算是基於元素級別的,並且基於numpy的廣播機制,在資料運算中,若維度不一致也能進行運算。numpy支援的算術運算很多,就不一一列舉了。

例子

>>> a
array([[1, 2, 3, 4],
       [2, 3, 4, 3]])
>>> b
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])
>>> c
array([5, 6, 4, 7])
>>> a+b   #元素對應相加
array([[ 1,  3,  5,  7],
       [ 6,  8, 10, 10]])
>>> a+c  #即使c只有一維,也會廣播到a的每一維進行計算
array([[ 6,  8,  7, 11],
       [ 7,  9,  8, 10]])

向量與矩陣的運算

標量、向量與矩陣之間相互的運算,在numpy中實現起來十分簡單,比如常見的點積(內積)、外積和叉乘。下面以兩個矩陣a、b和兩個向量c、d展示一下numpy在這些運算中的實現。

例子

>>> a                #2*4的矩陣
array([[1, 2, 3, 4],
       [2, 3, 4, 3]])
>>> b                 #2*4的矩陣
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])
>>> c                 #向量
array([5, 6, 4, 7])
>>> d = np.arange(2,6)
>>> d                  #向量
array([2, 3, 4, 5])
>>> np.dot(c,d)       #向量的內積是個標量
79
>>> np.dot(a,b.T)     #矩陣的點積,這裡將b轉置成4*2的矩陣(若使用np.inner便不需要將b轉置)
array([[20, 60],
       [20, 68]])
>>> np.dot(a,c)   #矩陣和向量的點積
array([57, 65])       
>>> np.outer(c,d)             #向量的外積
array([[10, 15, 20, 25],
       [12, 18, 24, 30],
       [ 8, 12, 16, 20],
       [14, 21, 28, 35]])
>>> np.outer(a,b)            #矩陣的外積
array([[ 0,  1,  2,  3,  4,  5,  6,  7],
       [ 0,  2,  4,  6,  8, 10, 12, 14],
       [ 0,  3,  6,  9, 12, 15, 18, 21],
       [ 0,  4,  8, 12, 16, 20, 24, 28],
       [ 0,  2,  4,  6,  8, 10, 12, 14],
       [ 0,  3,  6,  9, 12, 15, 18, 21],
       [ 0,  4,  8, 12, 16, 20, 24, 28],

對於矩陣,np.linalg中提供了矩陣的各種運算包括轉置、矩陣求逆等等。

例子 矩陣操作

#矩陣的轉置,直接在矩陣後面接個.T,如上的矩陣b
>>> b.T     #與 b.transpose()是等價的
array([[0, 4],
       [1, 5],
       [2, 6],
       [3, 7]])
#同時也可以對多維陣列進行轉置操作
>>> np.arange(12).reshape(2,2,3)
array([[[ 0,  1,  2],
        [ 3,  4,  5]],

       [[ 6,  7,  8],
        [ 9, 10, 11]]])
>>> np.arange(12).reshape(2,2,3).T  #2*2*3變成了3*2*2
array([[[ 0,  6],
        [ 3,  9]],

       [[ 1,  7],
        [ 4, 10]],

       [[ 2,  8],
        [ 5, 11]]])     
#為了便於計算,給定一個方陣i

>>> i = np.arange(9).reshape(3,3)
>>> i
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
>>> np.trace(i)  #矩陣的跡,即對角線上元素之和
12   
#使用 np.linalg 的eig計算出矩陣的特徵值和特徵向量
>>> t1,tv = np.linalg.eig(i)
>>> t1      #特徵值
array([  1.33484692e+01,  -1.34846923e+00,  -2.48477279e-16])
>>> tv      #特徵向量
array([[ 0.16476382,  0.79969966,  0.40824829],
       [ 0.50577448,  0.10420579, -0.81649658],
       [ 0.84678513, -0.59128809,  0.40824829]])   
#上面挖了個坑,這裡構造一個可逆的矩陣j
>>> j = np.array([[3,1,4],[3,3,5],[1,7,1]])
>>> j
array([[3, 1, 4],
       [3, 3, 5],
       [1, 7, 1]])
>>> np.linalg.det(j)    #求矩陣的行列式
-22.000000000000004
>>> np.linalg.inv(j)   #求矩陣的逆矩陣
array([[ 1.45454545, -1.22727273,  0.31818182],
       [-0.09090909,  0.04545455,  0.13636364],
       [-0.81818182,  0.90909091, -0.27272727]]) 
#對一個3*4的矩陣進行奇異值分解       
>>> u,s,v = np.linalg.svd(np.arange(12).reshape(3,4)) 
>>> u
array([[-0.1473065 , -0.90090739,  0.40824829],
       [-0.50027528, -0.2881978 , -0.81649658],
       [-0.85324407,  0.32451178,  0.40824829]])
>>> s
array([  2.24092982e+01,   1.95534034e+00,   7.68985043e-16])
>>> v
array([[-0.39390139, -0.46087474, -0.5278481 , -0.59482145],
       [ 0.73813393,  0.29596363, -0.14620666, -0.58837696],
       [-0.50775138,  0.52390687,  0.47544042, -0.4915959 ],
       [-0.20539847,  0.65232016, -0.68844492,  0.24152322]])

除此之外,linalg模組中還有許多操作,如:

 - norm            Vector or matrix norm
- inv             Inverse of a square matrix
- solve           Solve a linear system of equations
- det             Determinant of a square matrix
- lstsq           Solve linear least-squares problem
- pinv            Pseudo-inverse (Moore-Penrose) calculated using a singular
                  value decomposition
- eig             Eigenvalues and vectors of a square matrix
- eigh            Eigenvalues and eigenvectors of a Hermitian matrix
- eigvals         Eigenvalues of a square matrix
- eigvalsh        Eigenvalues of a Hermitian matrix
- qr              QR decomposition of a matrix
- svd             Singular value decomposition of a matrix
- cholesky        Cholesky decomposition of a matrix
- tensorsolve     Solve a linear tensor equation
- tensorinv       Calculate an inverse of a tensor

三、常用數學函式

numpy中包括豐富的數學函式和統計函式,比如三角函式、統計學上的均值、方差、標準差、相關係數等。

基本數學函式

如常見的求和、最小/最大值等(對於多維陣列,可指定對哪個軸進行計算)。下面以三維陣列n5求和為例說明一下關於軸的選擇。

例子

>>> n5           #這是一個3*2*4的陣列
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]],

       [[16, 17, 18, 19],
        [20, 21, 22, 23]]])
#對第一個軸求和,即相當於對第一維(長度為3)進行計算,得到的是2*4的陣列
#24=0+8+16,以此類推        
>>> np.sum(n5,axis=0)   
array([[24, 27, 30, 33],
       [36, 39, 42, 45]])
#對第二個軸求和,即相當於對第二維(長度為2)進行計算,得到的是3*4的陣列
#4=0+4,以此類推       
>>> np.sum(n5,axis=1)
array([[ 4,  6,  8, 10],
       [20, 22, 24, 26],
       [36, 38, 40, 42]])
#對第三個軸求和,即相當於對第三維(長度為4)進行計算,得到的是3*2的陣列
#6=0+1+2+3,以此類推       
>>> np.sum(n5,axis=2)
array([[ 6, 22],
       [38, 54],
       [70, 86]])
>>> np.sum(n5)  #所有元素的和
276       

三角函式

在numpy中使用三角函式也很方便,比如正弦np.sin;相應的,餘弦:cos,正切:tan,以及反函式arcsin、arccos和arctan。還有一些諸如雙曲函式,sinh、cosh、tanh等等。

例子

>>> np.sin(np.pi/2)   #計算pi的正弦值為1
1.0
#計算並畫出0到2*pi的正弦函式曲線
>>> e = np.arange(0,2*np.pi,0.1)
>>> import matplotlib.pyplot as plt
>>> plt.plot(e,np.sin(e))
[<matplotlib.lines.Line2D object at 0x0000000004D54C18>]
>>> plt.show()   #結果如圖

[圖片上傳失敗...(image-1d6edc-1544870342615)]

統計函式

比較常見的有均值(mean)、標準差(std)、方差(var)、中位數(median)、協方差(cov)、相關係數(corrcoef)等。同樣對於多維陣列,也可以指定根據哪個軸進行計算。

>>> n5
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]],

       [[16, 17, 18, 19],
        [20, 21, 22, 23]]])
>>> np.std(n5)
6.9221865524317288
>>> np.std(n5,axis=0)
array([[ 6.53197265,  6.53197265,  6.53197265,  6.53197265],
       [ 6.53197265,  6.53197265,  6.53197265,  6.53197265]])

小例子

使用numpy計算一個sigmoid函式的值並畫出函式影象。

>>> def sigmoid(x):
...     return 1.0/(1+np.e**(-x))
...
>>> x = np.arange(-5,5,0.01)
>>> plt.plot(x,sigmoid(x))
[<matplotlib.lines.Line2D object at 0x0000000008287AC8>]
>>> plt.show()  #如圖
2306872-b4fc9493a3c63805.png
sigmoid函式

結語

以上就是筆者工作中常用到的功能,特此記錄下來,便於以後查閱,如果能夠幫到需要的人再好不過了。雖然希望也盡力能夠全方位解析numpy在資料分析和科學計算上面的魅力,怎奈時間精力和個人知識經驗有限,實在不可能面面俱到,如果有欠缺的地方還望參考其他更多資料,如果能夠留言給予提醒那就再感激不過了。

相關文章