Python 100 Days Day69 Introduction to Numpy-2
25 Sep 2022 | beginner pythonauthor: jackfrued
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) |
檢查數組x和y元素是否幾乎相等 |
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) |
計算x和y的交集,返回這些元素構成的有序數組 |
union1d(x, y) |
計算x和y的並集,返回這些元素構成的有序數組 |
in1d(x, y) |
返回由判斷x 的元素是否在y中得到的布爾值構成的數組 |
setdiff1d(x, y) |
計算x和y的差集,返回這些元素構成的數組 |
setxor1d(x, y) |
計算x和y的對稱差,返回這些元素構成的數組 |
補充說明:在二維空間內,兩個向量$\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類。無論如何,利用這些已經封裝好的類和函數,我們可以輕松愉快的實現線性代數中很多的操作。
線性代數快速回顧
- 向量也叫矢量,是一個同時具有大小和方向,且滿足平行四邊形法則的幾何對象。與向量相對的概念叫標量或數量,標量只有大小、絕大多數情況下沒有方向。
- 向量可以進行加、減、數乘、點積、叉積等運算。
- 行列式由向量組成,它的性質可以由向量解釋。
- 行列式可以使用行列式公式計算:$det(\boldsymbol{A})=\sum_{n!} \pm {a_{1\alpha}a_{2\beta} \cdots a_{n\omega}}$。
- 高階行列式可以用代數余子式展開成多個低階行列式,如:$det(\boldsymbol{A})=a_{11}C_{11}+a_{12}C_{12}+ \cdots +a_{1n}C_{1n}$。
- 矩陣是由一系列元素排成的矩形陣列,矩陣里的元素可以是數字、符號或數學公式。
- 矩陣可以進行加法、減法、數乘、乘法、轉置等運算。
- 逆矩陣用$\boldsymbol{A^{-1}}$表示,$\boldsymbol{A}\boldsymbol{A^{-1}}=\boldsymbol{A^{-1}}\boldsymbol{A}=\boldsymbol{I}$;沒有逆矩陣的方陣是奇異矩陣。
- 如果一個方陣是滿秩矩陣(矩陣的秩等於矩陣的階數),該方陣對應的線性方程有唯一解。
說明:矩陣的秩是指矩陣中線性無關的行/列向量的最大個數,同時也是矩陣對應的線性變換的像空間的維度。
NumPy中矩陣相關函數
-
創建矩陣對象。
代碼:
# 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函數,而不是*運算符。 -
矩陣對象的屬性。
屬性 說明 A獲取矩陣對象對應的 ndarray對象A1獲取矩陣對象對應的扁平化後的 ndarray對象I可逆矩陣的逆矩陣 T矩陣的轉置 H矩陣的共軛轉置 shape矩陣的形狀 size矩陣元素的個數 -
矩陣對象的方法。
矩陣對象的方法跟之前講過的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.])
Comments