Skip to content

Latest commit

 

History

History
413 lines (309 loc) · 16.5 KB

69.NumPy的应用-2.md

File metadata and controls

413 lines (309 loc) · 16.5 KB

NumPy的應用-2

陣列的運算

使用 NumPy 最為方便的是當需要對陣列元素進行運算時,不用編寫迴圈程式碼遍歷每個元素,所有的運算都會自動的向量化(使用高效的、提前編譯的底層程式碼來對資料序列進行數學操作)。簡單的說就是,NumPy 中的數學運算和數學函式會自動作用於陣列中的每個成員。

陣列跟標量的運算

程式碼:

array35 = np.arange(1, 10)
print(array35 + 10)
print(array35 * 10)

輸出:

[11 12 13 14 15 16 17 18 19]
[10 20 30 40 50 60 70 80 90]

陣列跟陣列的運算

程式碼:

array36 = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3])
print(array35 + array36)
print(array35 * array36)
print(array35 ** array36)

輸出:

[ 2  3  4  6  7  8 10 11 12]
[ 1  2  3  8 10 12 21 24 27]
[  1   2   3  16  25  36 343 512 729]

通用一元函式

通用函式是對ndarray中的資料執行元素級運算的函式。你可以將其看做普通函式(接收一個標量值作為引數,返回一個標量值)的向量化包裝器,如下所示。

程式碼:

print(np.sqrt(array35))
print(np.log2(array35))

輸出:

[1.         1.41421356 1.73205081 2.         2.23606798 2.44948974
 2.64575131 2.82842712 3.        ]
[0.         1.         1.5849625  2.         2.32192809 2.5849625
 2.80735492 3.         3.169925  ]

表1:通用一元函式

函式 說明
abs / fabs 求絕對值的函式
sqrt 求平方根的函式,相當於array ** 0.5
square 求平方的函式,相當於array ** 2
exp 計算$e^x$的函式
log / log10 / log2 對數函式(e為底 / 10為底 / 2為底)
sign 符號函式(1 - 正數;0 - 零;-1 - 負數)
ceil / floor 上取整 / 下取整
isnan 返回布林陣列,NaN對應True,非NaN對應False
isfinite / isinf 判斷數值是否為無窮大的函式
cos / cosh / sin 三角函式
sinh / tan / tanh 三角函式
arccos / arccosh / arcsin 反三角函式
arcsinh / arctan / arctanh 反三角函式
rint / round 四捨五入函式

通用二元函式

程式碼:

array37 = np.array([[4, 5, 6], [7, 8, 9]])
array38 = np.array([[1, 2, 3], [3, 2, 1]])
print(array37 ** array38)
print(np.power(array37, array38))

輸出:

[[  4  25 216]
 [343  64   9]]
[[  4  25 216]
 [343  64   9]]

表2:通用二元函式

函式 說明
add(x, y) / substract(x, y) 加法函式 / 減法函式
multiply(x, y) / divide(x, y) 乘法函式 / 除法函式
floor_divide(x, y) / mod(x, y) 整除函式 / 求模函式
allclose(x, y) 檢查陣列xy元素是否幾乎相等
power(x, y) 陣列$x$的元素$x_i$和陣列$y$的元素$y_i$,計算$x_i^{y_i}$
maximum(x, y) / fmax(x, y) 兩兩比較元素獲取最大值 / 獲取最大值(忽略NaN)
minimum(x, y) / fmin(x, y) 兩兩比較元素獲取最小值 / 獲取最小值(忽略NaN)
dot(x, y) 點積運算(數量積,通常記為$\cdots$,用於歐幾里得空間(Euclidean space))
inner(x, y) 內積運算(內積的含義要高於點積,點積相當於是內積在歐幾里得空間$$的特例,而內積可以推廣到賦範向量空間,只要它滿足平行四邊形法則即可)
cross(x, y) 叉積運算(向量積,通常記為$\times$,運算結果是一個向量)
outer(x, y) 外積運算(張量積,通常記為$\bigotimes$,運算結果通常是一個矩陣)
intersect1d(x, y) 計算xy的交集,返回這些元素構成的有序陣列
union1d(x, y) 計算xy的並集,返回這些元素構成的有序陣列
in1d(x, y) 返回由判斷x 的元素是否在y中得到的布林值構成的陣列
setdiff1d(x, y) 計算xy的差集,返回這些元素構成的陣列
setxor1d(x, y) 計算xy的對稱差,返回這些元素構成的陣列

補充說明:在二維空間內,兩個向量$\boldsymbol{A}=\begin{bmatrix} a_1 \ a_2 \end{bmatrix}$和$\boldsymbol{B}=\begin{bmatrix} b_1 \ b_2 \end{bmatrix}$的叉積是這樣定義的:$\boldsymbol{A}\times \boldsymbol{B}=\begin{vmatrix} a_1 \quad a_2 \ b_1 \quad b_2 \end{vmatrix}=a_1b_2 - a_2b_1$,其中$\begin{vmatrix} a_1 \quad a_2 \ b_1 \quad b_2 \end{vmatrix}$稱為行列式。但是一定要注意,叉積並不等同於行列式,行列式的運算結果是一個標量,而叉積運算的結果是一個向量。如果不明白,我們可以看看三維空間兩個向量,$\boldsymbol{A}=\begin{bmatrix} a_1 \ a_2 \ a_3 \end{bmatrix}$和$\boldsymbol{B}=\begin{bmatrix} b_1 \ b_2 \ b_3 \end{bmatrix}$的叉積是$\left< \hat{i} \begin{vmatrix} a_2 \quad a_3 \ b_2 \quad b_3 \end{vmatrix}, -\hat{j} \begin{vmatrix} a_1 \quad a_3 \ b_1 \quad b_3 \end{vmatrix}, \hat{k} \begin{vmatrix} a_1 \quad a_2 \ b_1 \quad b_2 \end{vmatrix} \right>$,其中$\hat{i}, \hat{j}, \hat{k}$代表每個維度的單位向量。

廣播機制

上面的例子中,兩個二元運算的陣列形狀是完全相同的,我們再來研究一下,兩個形狀不同的陣列是否可以直接做二元運算或使用二元函式進行運算,請看下面的例子。

程式碼:

array39 = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]])
array40 = np.array([1, 2, 3])
array39 + array40

輸出:

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

程式碼:

array41 = np.array([[1], [2], [3], [4]])
array39 + array41

輸出:

array([[1, 1, 1],
       [3, 3, 3],
       [5, 5, 5],
       [7, 7, 7]])

透過上面的例子,我們發現形狀不同的陣列仍然有機會進行二元運算,但也絕對不是任意的陣列都可以進行二元運算。簡單的說,只有兩個陣列後緣維度相同或者其中一個數組後緣維度為1時,廣播機制會被觸發,而透過廣播機制如果能夠使兩個陣列的形狀一致,才能進行二元運算。所謂後緣維度,指的是陣列shape屬性對應的元組中最後一個元素的值(從後往前數最後一個維度的值),例如,我們之前開啟的影象對應的陣列後緣維度為3,3行4列的二維陣列後緣維度為4,而有5個元素的一維陣列後緣維度為5。簡單的說就是,後緣維度相同或者其中一個數組的後緣維度為1,就可以應用廣播機制;而廣播機制如果能夠使得陣列的形狀一致,就滿足了兩個陣列對應元素做運算的需求,如下圖所示。

其他常用函式

除了上面講到的函式外,NumPy 中還提供了很多用於處理陣列的函式,ndarray物件的很多方法也可以透過直接呼叫函式來實現,下表給出了一些常用的函式。

表3:NumPy其他常用函式

函式 說明
unique 去除陣列重複元素,返回唯一元素構成的有序陣列
copy 返回複製陣列得到的陣列
sort 返回陣列元素排序後的複製
split / hsplit / vsplit 將陣列拆成若干個子陣列
stack / hstack / vstack 將多個數組堆疊成新陣列
concatenate 沿著指定的軸連線多個數組構成新陣列
append / insert 向陣列末尾追加元素 / 在陣列指定位置插入元素
argwhere 找出陣列中非0元素的位置
extract / select / where 按照指定的條件從陣列中抽取或處理陣列元素
flip 沿指定的軸翻轉陣列中的元素
fromiter 透過迭代器建立陣列物件
fromregex 透過讀取檔案和正則表示式解析獲取資料建立陣列物件
repeat / tile 透過對元素的重複來建立新陣列
roll 沿指定軸對陣列元素進行移位
resize 重新調整陣列的大小
place / put 將陣列中滿足條件的元素/指定的元素替換為指定的值
partition 用選定的元素對陣列進行一次劃分並返回劃分後的陣列

提示:上面的resize函式和ndarray物件的resize方法是有區別的,resize函式在調整陣列大小時會重複陣列中的元素作為填補多出來的元素的值,而ndarry物件的resize方法是用0來填補多出來的元素。這些小細節不清楚暫時也不要緊,但是如果用到對應的功能了就要引起注意。

程式碼:

array42 = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])
array43 = np.array([[4, 4, 4], [5, 5, 5], [6, 6, 6]])
np.hstack((array42, array43))

輸出:

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

程式碼:

np.vstack((array42, array43))

輸出:

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

程式碼:

np.concatenate((array42, array43))

輸出:

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

程式碼:

np.concatenate((array42, array43), axis=1)

輸出:

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

矩陣運算

NumPy 中提供了專門用於線性代數(linear algebra)的模組和表示矩陣的型別matrix,當然我們透過二維陣列也可以表示一個矩陣,官方並不推薦使用matrix類而是建議使用二維陣列,而且有可能在將來的版本中會移除matrix類。無論如何,利用這些已經封裝好的類和函式,我們可以輕鬆愉快的實現線性代數中很多的操作。

線性代數快速回顧

  1. 向量也叫向量,是一個同時具有大小和方向,且滿足平行四邊形法則的幾何物件。與向量相對的概念叫標量數量,標量只有大小、絕大多數情況下沒有方向。
  2. 向量可以進行數乘點積叉積等運算。
  3. 行列式由向量組成,它的性質可以由向量解釋。
  4. 行列式可以使用行列式公式計算:$det(\boldsymbol{A})=\sum_{n!} \pm {a_{1\alpha}a_{2\beta} \cdots a_{n\omega}}$。
  5. 高階行列式可以用代數餘子式展開成多個低階行列式,如:$det(\boldsymbol{A})=a_{11}C_{11}+a_{12}C_{12}+ \cdots +a_{1n}C_{1n}$。
  6. 矩陣是由一系列元素排成的矩形陣列,矩陣裡的元素可以是數字、符號或數學公式。
  7. 矩陣可以進行加法減法數乘乘法轉置等運算。
  8. 逆矩陣用$\boldsymbol{A^{-1}}$表示,$\boldsymbol{A}\boldsymbol{A^{-1}}=\boldsymbol{A^{-1}}\boldsymbol{A}=\boldsymbol{I}$;沒有逆矩陣的方陣是奇異矩陣
  9. 如果一個方陣是滿秩矩陣(矩陣的秩等於矩陣的階數),該方陣對應的線性方程有唯一解。

說明矩陣的秩是指矩陣中線性無關的行/列向量的最大個數,同時也是矩陣對應的線性變換的像空間的維度。

NumPy中矩陣相關函式

  1. 建立矩陣物件。

    程式碼:

    # matrix建構函式可以傳入類陣列物件也可以傳入字串
    m1 = np.matrix('1 2 3; 4 5 6')
    m1

    輸出:

    matrix([[1, 2, 3],
            [4, 5, 6]])
    

    程式碼:

    # asmatrix函式也可以寫成mat函式,它們其實是同一個函式
    m2 = np.asmatrix(np.array([[1, 1], [2, 2], [3, 3]]))
    m2

    輸出:

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

    程式碼:

    m1 * m2

    輸出:

    matrix([[14, 14],
            [32, 32]])
    

    說明:注意matrix物件和ndarray物件乘法運算的差別,如果兩個二維陣列要做矩陣乘法運算,應該使用@運算子或matmul函式,而不是*運算子。

  2. 矩陣物件的屬性。

    屬性 說明
    A 獲取矩陣物件對應的ndarray物件
    A1 獲取矩陣物件對應的扁平化後的ndarray物件
    I 可逆矩陣的逆矩陣
    T 矩陣的轉置
    H 矩陣的共軛轉置
    shape 矩陣的形狀
    size 矩陣元素的個數
  3. 矩陣物件的方法。

矩陣物件的方法跟之前講過的ndarray陣列物件的方法基本差不多,此處不再進行贅述。

NumPy的線性代數模組

NumPy 的linalg模組中有一組標準的矩陣分解運算以及諸如求逆和行列式之類的函式,它們跟 MATLAB 和 R 等語言所使用的是相同的行業標準線性代數庫,下面的表格列出了numpy以及linalg模組中常用的跟線性代數相關的函式。

函式 說明
diag 以一維陣列的形式返回方陣的對角線元素或將一維陣列轉換為方陣(非對角元素元素為0)
vdot 向量的點積
dot 陣列的點積
inner 陣列的內積
outer 陣列的叉積
trace 計算對角線元素的和
norm 求模(範數)運算
det 計算行列式的值(在方陣上計算會得到一個標量)
matrix_rank 計算矩陣的秩
eig 計算矩陣的特徵值(eigenvalue)和特徵向量(eigenvector)
inv 計算非奇異矩陣($n$階方陣)的逆矩陣
pinv 計算矩陣的摩爾-彭若斯(Moore-Penrose)廣義逆
qr QR分解(把矩陣分解成一個正交矩陣與一個上三角矩陣的積)
svd 計算奇異值分解(singular value decomposition)
solve 解線性方程組$\boldsymbol{A}\boldsymbol{x}=\boldsymbol{b}$,其中$\boldsymbol{A}$是一個方陣
lstsq 計算$\boldsymbol{A}\boldsymbol{x}=\boldsymbol{b}$的最小二乘解

大家如果有興趣可以用下面的程式碼驗證上面的函式。

程式碼:

m3 = np.array([[1., 2.], [3., 4.]])
np.linalg.inv(m3)

輸出:

array([[-2. ,  1. ],
       [ 1.5, -0.5]])

程式碼:

m4 = np.array([[1, 3, 5], [2, 4, 6], [4, 7, 9]])
np.linalg.det(m4)

輸出:

2

程式碼:

# 解線性方程組ax=b
# 3*x1 + x2= 9,x1 + 2*x2 = 8
a = np.array([[3,1], [1,2]])
b = np.array([9, 8])
np.linalg.solve(a, b)

輸出:

array([2., 3.])