使用 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}$;沒有逆矩陣的方陣是奇異矩陣。
- 如果一個方陣是滿秩矩陣(矩陣的秩等於矩陣的階數),該方陣對應的線性方程有唯一解。
說明:矩陣的秩是指矩陣中線性無關的行/列向量的最大個數,同時也是矩陣對應的線性變換的像空間的維度。
-
建立矩陣物件。
程式碼:
# 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 的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.])