目錄
前提:導入numpy庫。
import numpy as np
一、數組的創建
1.1 創建數組
創建numpy數組的三種方式:
- 按步就班法:np.array() 用在列表和元組上
- 定隔定點法:np.arange() 和 np.linspace()
- 一步登天法:np.ones(),np.zeros(),np.eye() 和 np.random.random()
1. 按步就班法
list = [1, 2, 3.5, 5.5, 8]
np.array(list)
array([1. , 2. , 3.5, 5.5, 8. ])
tuple = (1, 2, 3.5, 5.5, 8)
print(np.array(tuple))
np.array(tuple)
[1. 2. 3.5 5.5 8. ]
array([1. , 2. , 3.5, 5.5, 8. ])
注意,numpy數組的輸出都帶有 array() 的字樣,裏面的元素用“中括號 []”框住。用函數 print 打印 numpy 數組就沒有 array() 的字樣了。
2. 定隔定點法
- 定隔的 arange(): 固定元素大小間隔
- 定點的 linspace():固定元素個數
函數 arange 的參數爲起點,終點,間隔:arange(start , stop , step)。其中 stop 必須要有,start 和 step 沒有的話默認爲 1。
print(np.arange(10))
print(np.arange(2, 10))
print(np.arange(2, 10, 2))
[0 1 2 3 4 5 6 7 8 9]
[2 3 4 5 6 7 8 9]
[2 4 6 8]
函數 linspace 的參數爲起點,終點,點數:linspace (start , stop , num)。其中 start 和 stop 必須要有,num 沒有的話默認爲 50。
print(np.linspace(2, 6, 3))
print(np.linspace(2, 8, 4))
print(np.linspace(0, 49))
[2. 4. 6.]
[2. 4. 6. 8.]
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.
18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35.
36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49.]
3. 一步登天法
- zeros(): 創建全是 0 的 n 維數組
- ones(): 創建全是 1 的 n 維數組
- random():創建隨機 n 維數組
- eye(): 創建對角矩陣
前三種,由於輸出是 n 爲數組,它們的參數是一個「標量」或「元組類型的形狀」。
print(np.zeros(5)) # 標量5代表形狀(5,)
print(np.ones((2, 3)))
print(np.random.random((2, 3, 4)))
[0. 0. 0. 0. 0.]
[[1. 1. 1.]
[1. 1. 1.]]
[[[0.42180299 0.84854733 0.115058 0.65318331]
[0.03357884 0.05289622 0.19615066 0.22481889]
[0.05550249 0.2390295 0.24496322 0.83995308]]
[[0.73575253 0.19963972 0.3201794 0.75923466]
[0.54934324 0.6029062 0.39607958 0.9521938 ]
[0.10393022 0.71187835 0.04202606 0.85566354]]]
函數 eye(),它的參數就是一個標量,控制矩陣的行數或列數。
eye() 裏面的參數 k
- k = 0: 默認設置,代表 1 落在對角線上
- k = 1: 代表 1 落在對角線右上方
- k = -1:代表 1 落在對角線左下方
print(np.eye(4))
print(np.eye(4, k=1))
[[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
[[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]
[0. 0. 0. 0.]]
1.2 數組的性質
先用按部就班的 np.array() 帶列表生成二維數組 arr。
arr = np.array([1, 2, 3.5, 5.5, 8])
再用 dir(arr) 來查看數組的屬性,之後我們對 type,ndim,len(),size,shape,stride,dtype 這幾個常用函數分析一波。
- type: 數組類型,當然是 numpy.ndarray
- ndim: 數組維度個數
- len(): 數組長度
- size: 數組元素個數
- shape: 數組形狀,即每個維度的元素個數 (用元組來表示),只有一維,元素個數爲 5,寫成元組形式是 (5,)
- strides:數組跨度,即在某一維度下爲了獲取到下一個元素需要「跨過」的字節數 (用元組來表示),float64 是 8 個字節數 (bytes),因此跨度爲 8
- dtype: 數組元素類型 (注意和 type 區分)
print('類型:', type(arr))
print('維度:', arr.ndim)
print('長度:', len(arr))
print('個數:', arr.size)
print('形狀:', arr.shape)
print('跨度:', arr.strides)
print('類型:', arr.dtype)
類型: <class 'numpy.ndarray'>
維度:
長度: 5
個數: 5
形狀: (5,)
跨度: (8,)
類型: float64
二維數組,甚至多維數組,大家可自行測試體會。
二、數組的存載
2.1 numpy 自身的 .npy 格式
- save:np.save( ‘’文件名”,數組 ) 即可保存爲 .npy 格式
- load:np.load( "文件名" ) 即可加載該文件
arr_disk = np.arange(8)
np.save("arr_disk", arr_disk) # 保存在當前目錄下
np.load("arr_disk.npy")
array([0, 1, 2, 3, 4, 5, 6, 7])
2.2 文本 .txt 格式
- savetxt:用 np.savetxt( ‘’文件名”,數組 ) 即可保存爲 .txt 格式
- loadtxt: 用 np.loadtxt( "文件名" ) 即可加載該文件
arr_text = np.array([[1., 2., 3.], [4., 5., 6.]])
np.savetxt("arr_text.txt", arr_text) # 保存爲當前目錄下
np.loadtxt("arr_text.txt")
array([[1., 2., 3.],
[4., 5., 6.]])
2.3 文本 .csv 格式
arr_csv = np.array([[1, 2, 3], [4, 5, 6]])
np.savetxt("arr_csv.csv", arr_csv) # 保存爲當前目錄下
np.loadtxt("arr_csv.csv")
# 或 np.genfromtxt("arr_csv.csv")
array([[1., 2., 3.],
[4., 5., 6.]])
三、數組的獲取
獲取數組是通過索引 (indexing) 和切片 (slicing) 來完成的。
- 索引:獲取一個特定位置的元素,寫法是 arr[index]
- 切片:獲取一段特定位置的元素,寫法是 arr[start : stop : step]
切片的操作是可以用索引操作來實現的 (一個一個總能湊成一段),只是沒必要罷了。爲了簡化,我們在本章三節標題裏把切片和索引都叫做索引。索引數組有三種形式,正規索引 (normal indexing)、布爾索引 (boolean indexing) 和花式索引 (fancy indexing)。
3.1 正規索引
一維數組:
arr1 = np.arange(10) # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(arr1[6]) # 索引
print(arr1[1:5]) # 切片
6
[1 2 3 4]
二維數組:
arr2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr2)
print(arr2[2]) # 索引第三行
print(arr2[0][2]) # 索引第一行第三列
print(arr2[0, 2]) # 索引第一行第三列
[[1 2 3]
[4 5 6]
[7 8 9]]
[7 8 9]
3
3
print(arr2[:2]) # 切片前兩行
print(arr2[:, [0, 2]]) # 切片第一列和第三列
print(arr2[1, :2]) # 切片第二行的前兩個元素
print(arr2[:2, 2]) # 切片第三列的前兩個元素
[[1 2 3]
[4 5 6]]
[[1 3]
[4 6]
[7 9]]
[4 5]
[3 6]
3.2 布爾索引
布爾索引,就是用一個由布爾 (boolean) 類型值組成的數組來選擇元素的方法。
code = np.array(['BABA', 'FB', 'JD', 'BABA', 'JD', 'FB'])
price = np.array([[170,177,169], [150,159,153],
[24,27,26], [165,170,167],
[22,23,20], [155,116,157]])
print(code == 'BABA')
print(price[code == 'BABA'])
print(price[code == 'BABA', :1])
注:這種布爾索引的操作在 Pandas 更常用也更方便。
3.3 花式索引
花式索引是獲取數組中想要的特定元素的有效方法。
arr = np.arange(32).reshape(8,4)
print(arr)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]
[16 17 18 19]
[20 21 22 23]
[24 25 26 27]
[28 29 30 31]]
print(arr[[4,3,6]], '\n') # 按特定順序來獲取第 5,4 和 7 行
print(arr[[-4,-3,-6]], '\n') # 按特定順序來獲取倒數第 4,3 和 6 行
print(arr[[1,5,7,2], [0,3,1,2]]) # 獲取第二行第一列、第六行第四列、第八行第二列、第三行第三列的元素
# print(np.array([arr[1,0], arr[5,3], arr[7,1], arr[2,2]]))
[[16 17 18 19]
[12 13 14 15]
[24 25 26 27]]
[[16 17 18 19]
[20 21 22 23]
[ 8 9 10 11]]
[ 4 23 29 10]
print(arr[:,[0,3,1,2]]) # 把原先的 [0,1,2,3] 的列換成 [0,3,1,2]
[[ 0 3 1 2]
[ 4 7 5 6]
[ 8 11 9 10]
[12 15 13 14]
[16 19 17 18]
[20 23 21 22]
[24 27 25 26]
[28 31 29 30]]
四、數組的變形
本節介紹四大類數組層面上的操作,具體有:
- 重塑 (reshape) 和打平 (ravel, flatten)
- 合併 (concatenate, stack) 和分裂 (split)
- 重複 (repeat) 和拼接 (tile)
- 其他操作 (sort, insert, delete, copy)
4.1 重塑和打平
重塑 (reshape) 和打平 (ravel, flatten) 這兩個操作僅僅只改變數組的維度:
- 重塑:是從低維到高維
- 打平:是從高維到低維
1. 重塑
用reshape()函數將一維數組 arr 重塑成二維數組。
arr = np.arange(12)
print(arr)
print(arr.reshape((4, 3)))
[ 0 1 2 3 4 5 6 7 8 9 10 11]
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
當你重塑高維矩陣時,不想花時間算某一維度的元素個數時,可以用「-1」取代,程序會自動幫你計算出來。比如把 12 個元素重塑成 (2, 6),你可以寫成 (2, -1) 或者 (-1, 6)。
print(arr.reshape((2, -1)))
print(arr.reshape((-1, 6)))
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
注意:
在衆多計算機語言中,
-
默認行主序的有 C 語言( order=‘C’ 等價於行主序)
-
默認列主序的有 Fortran 語言( order=‘F’ 等價於列主序)
在 numpy 數組中,默認的是行主序,即 order ='C'。如果你真的想在「重塑」和「打平」時用列主序,只用把 order 設爲 'F',以重塑舉例:
arr = np.arange(12)
print(arr.reshape((4, 3)))
print(arr.reshape((4,3), order='F'))
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
[[ 0 4 8]
[ 1 5 9]
[ 2 6 10]
[ 3 7 11]]
2. 打平
用 ravel() 或flatten() 函數將二維數組 arr 打平成一維數組。
arr = np.arange(12).reshape((4,3))
print(arr)
ravel_arr = arr.ravel()
print(ravel_arr)
flatten_arr = arr.flatten()
print(flatten_arr)
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
函數 ravel() 或 flatten() 的不同之處是:
- ravel() 按「行主序」打平時沒有複製原數組,按「列主序」在打平時複製了原數組
- flatten() 在打平時複製了原數組
4.2 合併和分裂
合併 (concatenate, stack) 和分裂 (split) 這兩個操作僅僅只改變數組的分合:
- 合併:是多合一
- 分裂:是一分多
1. 合併
使用「合併」函數有三種選擇:
- 有通用的 concatenate
- 有專門的 vstack, hstack, dstack
- 有極簡的 r_, c_
用下面兩個數組來舉例:
arr1 = np.array([[1, 2, 3], [4, 5, 6]])
arr2 = np.array([[7, 8, 9], [10, 11, 12]])
在 concatenate() 函數裏通過設定軸,來對數組進行豎直方向合併 (軸 0) 和水平方向合併 (軸 1)。
print(np.concatenate([arr1, arr2], axis=0))
print(np.concatenate([arr1, arr2], axis=1))
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
[[ 1 2 3 7 8 9]
[ 4 5 6 10 11 12]]
在 NumPy 裏還有專門合併的函數
- vstack:v 代表 vertical,豎直合併,等價於 concatenate(axis=0)
- hstack:h 代表 horizontal,水平合併,等價於 concatenate(axis=1)
- dstack:d 代表 depth-wise,按深度合併,深度有點像彩色照片的 RGB 通道
print(np.vstack((arr1, arr2)))
print(np.hstack((arr1, arr2)))
print(np.dstack((arr1, arr2)))
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
[[ 1 2 3 7 8 9]
[ 4 5 6 10 11 12]]
[[[ 1 7]
[ 2 8]
[ 3 9]]
[[ 4 10]
[ 5 11]
[ 6 12]]]
和 vstack, hstack 不同,dstack 將原數組的維度增加了一維。
np.dstack((arr1, arr2)).shape
(2, 3, 2)
此外,還有一種更簡單的在豎直和水平方向合併的函數,r_() 和 c_()。
print(np.r_[arr1,arr2])
print(np.c_[arr1,arr2])
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
[[ 1 2 3 7 8 9]
[ 4 5 6 10 11 12]]
2. 分裂
使用「分裂」函數有兩種選擇
- 有通用的 split
- 有專門的 hsplit, vsplit
用下面數組來舉例:
arr = np.arange(25).reshape((5,5))
print(arr)
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]
[20 21 22 23 24]]
和 concatenate() 函數一樣,我們可以在 split() 函數裏通過設定軸,來對數組沿着豎直方向分裂 (軸 0) 和沿着水平方向分裂 (軸 1)。
first, second, third = np.split(arr, [1,3])
print('The first split is', first) # 第 1 行
print('The second split is', second) # 第 2 到 3 行
print('The third split is', third) # 第 4 到 5 行
The first split is [[0 1 2 3 4]]
The second split is [[ 5 6 7 8 9]
[10 11 12 13 14]]
The third split is [[15 16 17 18 19]
[20 21 22 23 24]]
split() 默認沿着軸 0 分裂,其第二個參數 [1, 3] 相當於是個切片操作,將數組分成三部分。
vsplit() 和 split(axis=0) 等價,hsplit() 和 split(axis=1) 等價。
first, second, third = np.hsplit(arr, [1, 3])
print('The first split is', first)
print('The second split is', second)
print('The third split is', third)
The first split is [[ 0]
[ 5]
[10]
[15]
[20]]
The second split is [[ 1 2]
[ 6 7]
[11 12]
[16 17]
[21 22]]
The third split is [[ 3 4]
[ 8 9]
[13 14]
[18 19]
[23 24]]
4.3 重複和拼接
重複 (repeat) 和拼接 (tile) 這兩個操作本質都是複製。
- 重複:是在元素層面複製
- 拼接:是在數組層面複製
1. 重複
函數 repeat() 複製的是數組的每一個元素,參數有幾種設定方法:
- 一維數組:用標量和列表來複制元素的個數
- 多維數組:用標量和列表來複制元素的個數,用軸來控制複製的行和列
arr = np.arange(5)
print(arr)
print(arr.repeat(3)) # 數組 arr 中每個元素複製 3 遍
print(arr.repeat([1, 2, 3, 4, 5])) # 數組 arr 中每個元素分別複製 1, 2, 3, 4, 5 遍
[0 1 2 3 4]
[0 0 0 1 1 1 2 2 2 3 3 3 4 4 4]
[0 1 1 2 2 2 3 3 3 3 4 4 4 4 4]
arr2 = np.arange(6).reshape((2,3))
print(arr2)
print(arr2.repeat(2, axis=0)) # 數組 arr2 中每個元素沿着軸 0 複製 2 遍
print(arr2.repeat([2,3,4], axis=1)) # 數組 arr2 中每個元素沿着軸 1 分別複製 2, 3, 4 遍
[[0 1 2]
[3 4 5]]
[[0 1 2]
[0 1 2]
[3 4 5]
[3 4 5]]
[[0 0 1 1 1 2 2 2 2]
[3 3 4 4 4 5 5 5 5]]
2. 拼接
函數 tile() 複製的是數組本身,參數有幾種設定方法:
- 標量:把數組當成一個元素,一列一列複製
- 形狀:把數組當成一個元素,按形狀複製
arr2 = np.arange(6).reshape((2,3))
print(arr2)
print(np.tile(arr2, 2)) # 數組 arr2 按列複製 2 遍
print(np.tile(arr2, (2, 3))) # 數組 arr2 按形狀複製 6 (2×3) 遍,並以 (2,3) 的形式展現
[[0 1 2]
[3 4 5]]
[[0 1 2 0 1 2]
[3 4 5 3 4 5]]
[[0 1 2 0 1 2 0 1 2]
[3 4 5 3 4 5 3 4 5]
[0 1 2 0 1 2 0 1 2]
[3 4 5 3 4 5 3 4 5]]
4.4 其他操作
本節討論數組的其他操作,包括排序 (sort),插入 (insert),刪除 (delete) 和複製 (copy)。
1. 排序
排序包括直接排序 (direct sort) 和間接排序 (indirect sort)。
直接排序
arr = np.array([5, 3, 2, 6, 1, 4])
arr.sort()
print(arr)
[1 2 3 4 5 6]
sort()函數是按升序 (ascending order) 排列的,該函數裏沒有參數可以控制 order,因此你想要按降序排列的數組,只需:
print(arr[::-1])
[6 5 4 3 2 1]
注意:
用來排序 numpy 用兩種方式:
- arr.sort():sort 會改變 arr
- np.sort(arr):sort 在排序時創建了 arr 的一個複製品,不會改變 arr
間接排序
有時候我們不僅僅只想排序數組,還想在排序過程中提取每個元素在原數組對應的索引(index),這時 argsort() 就派上用場了。
score = np.array([100, 60, 99, 80, 91])
idx = score.argsort()
print(idx) # 打印數據由小到大的地址
print(score[idx])
[1 3 4 2 0]
[ 60 80 91 99 100]
arr = np.random.randint(40, size=(3,4))
print(arr)
print(arr[:, arr[0].argsort()]) # 對其第一行 arr[0] 排序,獲取索引,在應用到所用行上
[[24 16 39 34]
[10 5 29 35]
[39 22 17 15]]
[[16 24 34 39]
[ 5 10 35 29]
[22 39 15 17]]
2. 插入和刪除
和列表一樣,我們可以給 numpy 數組:
- 用insert()函數在某個特定位置之前插入元素
- 用delete()函數刪除某些特定元素
a = np.arange(6)
print(a)
print(np.insert(a, 1, 100))
print(np.delete(a, [1, 3]))
[0 1 2 3 4 5]
[ 0 100 1 2 3 4 5]
[0 2 4 5]
3. 複製
用copy()函數來複制數組 a 得到 a_copy,很明顯,改變 a_copy 裏面的元素不會改變 a。
a = np.arange(6)
a_copy = a.copy()
print('Before changing value, a is', a)
print('Before changing value, a_copy is', a_copy)
a_copy[-1] = 99
print('After changing value, a_copy is', a_copy)
print('After changing value, a is', a)
Before changing value, a is [0 1 2 3 4 5]
Before changing value, a_copy is [0 1 2 3 4 5]
After changing value, a_copy is [ 0 1 2 3 4 99]
After changing value, a is [0 1 2 3 4 5]
五、數組的計算
本節介紹四大類數組計算,具體有:
- 元素層面 (element-wise) 計算
- 線性代數 (linear algebra) 計算
- 元素整合 (element aggregation) 計算
- 廣播機制 (broadcasting) 計算
5.1 元素層面計算
Numpy 數組元素層面計算包括:
- 二元運算 (binary operation):加減乘除
- 數學函數:倒數、平方、指數、對數
- 比較運算 (comparison)
先定義兩個數組 arr1 和 arr2。
arr1 = np.array([[1., 2., 3.], [4., 5., 6.]])
arr2 = np.ones((2,3)) * 2
print(arr1)
print(arr2)
[[1. 2. 3.]
[4. 5. 6.]]
[[2. 2. 2.]
[2. 2. 2.]]
# 加減乘除
print(arr1 + arr2 + 1)
print(arr1 - arr2)
print(arr1 * arr2)
print(arr1 / arr2)
# 倒數、平方、指數、對數
print(1 / arr1)
print(arr1 ** 2)
print(np.exp(arr1))
print(np.log(arr1))
# 比較
print(arr1 > arr2)
print(arr1 > 3)
[[4. 5. 6.]
[7. 8. 9.]]
[[-1. 0. 1.]
[ 2. 3. 4.]]
[[ 2. 4. 6.]
[ 8. 10. 12.]]
[[0.5 1. 1.5]
[2. 2.5 3. ]]
[[1. 0.5 0.33333333]
[0.25 0.2 0.16666667]]
[[ 1. 4. 9.]
[16. 25. 36.]]
[[ 2.71828183 7.3890561 20.08553692]
[ 54.59815003 148.4131591 403.42879349]]
[[0. 0.69314718 1.09861229]
[1.38629436 1.60943791 1.79175947]]
[[False False True]
[ True True True]]
[[False False False]
[ True True True]]
5.2 線性代數計算
在 NumPy 默認不採用矩陣運算,而是數組 (ndarray) 運算。矩陣只是二維,而數組可以是任何維度,因此數組運算更通用些。
注:arr 的形狀是 (2,),只含一個元素的元組只說明 arr 是一維,數組是不分行數組或列數組的。
下面我們分別對「數組」和「矩陣」從創建、轉置、求逆和相乘四個方面看看它們的同異。
1. 創建
創建數組 arr2d 和矩陣 A,注意它們的輸出有 array 和 matrix 的關鍵詞。
arr2 = np.array([[1, 2], [3, 1]])
arr2
array([[1, 2],
[3, 1]])
A = np.asmatrix(arr2)
matrix([[1, 2],
[3, 1]])
2. 轉置
數組用 arr2.T 操作或 arr.tranpose() 函數,而矩陣用 A.T 操作。主要原因就是 .T 只適合二維數據。
print(arr2.T)
print(arr2.transpose())
print(A.T)
[[1 3]
[2 1]]
[[1 3]
[2 1]]
[[1 3]
[2 1]]
3. 求逆
數組用 np.linalg.inv() 函數,而矩陣用 A.I 和 A**-1 操作。
print(np.linalg.inv(arr2))
print(A.I)
print(A**-1)
[[-0.2 0.4]
[ 0.6 -0.2]]
[[-0.2 0.4]
[ 0.6 -0.2]]
[[-0.2 0.4]
[ 0.6 -0.2]]
4. 相乘
相乘是個很模棱兩可的概念
- 數組相乘是在元素層面進行,
- 矩陣相乘要就是數學定義的矩陣相乘 (比如第一個矩陣的列要和第二個矩陣的行一樣)
arr2 = np.array([[1, 2], [3, 1]])
A = np.asmatrix(arr2)
print(arr2*arr2) # 數組相乘
print(A*A) # 矩陣相乘
[[1 4]
[9 1]]
[[7 4]
[6 7]]
點乘函數 dot()
點乘左右兩邊最常見的數組就是:
- 向量 (1D) 和向量 (1D)
- 矩陣 (2D) 和向量 (1D)
- 矩陣 (2D) 和矩陣 (2D)
分別看看三個簡單例子。
例一:np.dot(向量, 向量) 實際上做的就是內積,即把兩個向量每個元素相乘,最後再加總。點乘結果 10 是個標量 (0D 數組),形狀 = ()。
x = np.array([1, 2, 3])
y = np.array([3, 2, 1])
z = np.dot(x, y)
print(z.shape)
print(z)
()
10
例二:np.dot(矩陣, 向量) 實際上做的就是普通的矩陣乘以向量。點乘結果是個向量 (1D 數組),形狀 = (2, )。
x = np.array([1, 2, 3])
y = np.array([[3, 2, 1], [1, 1, 1]])
z = np.dot(y, x)
print(z.shape)
print(z)
(2,)
[10 6]
例三:np.dot(矩陣, 矩陣) 實際上做的就是普通的矩陣乘以矩陣。點乘結果是個矩陣 (2D 數組),形狀 = (2, 3)。
x = np.array([[1, 2, 3], [1, 2, 3], [1, 2, 3]])
y = np.array([[3, 2, 1], [1, 1, 1]])
z = np.dot(y, x)
print(z.shape)
print(z)
(2, 3)
[[ 6 12 18]
[ 3 6 9]]
當 x 第二個維度的元素 (x.shape[1]) 和 y 第一個維度的元素 (y.shape[0]) 個數相等時,np.dot(X, Y) 纔有意義。
5.3 元素整合計算
在數組中,元素可以以不同方式整合 (aggregation)。拿求和 (sum) 函數來說,我們可以對數組:
- 所有的元素求和
- 在某個軸 (axis) 上的元素求和
arr = np.arange(1, 7).reshape((2, 3))
print(arr)
print( '總和:', arr.sum() )
print( '跨行求和', arr.sum(axis=0) )
print( '跨列求和', arr.sum(axis=1) )
[[1 2 3]
[4 5 6]]
總和: 21
跨行求和 [5 7 9]
跨列求和 [ 6 15]
行和列這些概念對矩陣 (二維矩陣) 才適用,高維矩陣還是要用軸 (axis) 來區分每個維度。
除了 sum 函數,整合函數還包括 min, max, mean, std 和 cumsum,分別是求最小值、最大值、均值、標準差和累加,這些函數對數組裏的元素整合方式和 sum 函數相同,就不多講了。
5.4 廣播機制計算
當對兩個形狀不同的數組按元素操作時,可能會觸發「廣播機制」。具體做法,先適當複製元素使得這兩個數組形狀相同後再按元素操作,兩個步驟:
- 廣播軸 (broadcast axis):比對兩個數組的維度,將形狀小的數組的維度 (軸) 補齊
- 複製元素:順着補齊的軸,將形狀小的數組裏的元素複製,使得最終形狀和另一個數組吻合
arr = np.arange(12).reshape((4,3))
print(arr)
print(arr.mean(axis=0))
print(arr - arr.mean(axis=0))
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
[4.5 5.5 6.5]
[[-4.5 -4.5 -4.5]
[-1.5 -1.5 -1.5]
[ 1.5 1.5 1.5]
[ 4.5 4.5 4.5]]
沿軸 0 的均值的一維數組被廣播到數組 arr 的所有的行上。
廣播機制的規則
當我們對兩個數組操作時,如果它們的形狀
- 不相容 (incompatible),廣播機制不能進行
- 相容 (compatible),廣播機制可以進行
因此,進行廣播機制分兩步
- 檢查兩個數組形狀是否兼容,即從兩個形狀元組最後一個元素,來檢查(它們是否相等,是否有一個等於 1)。
- 一旦它們形狀兼容,確定兩個數組的最終形狀。
例一:維度一樣,形狀不一樣
a = np.array([[1, 2, 3]])
b = np.array([[4], [5], [6]])
print('The shape of a is', a.shape)
print('The shape of b is', b.shape)
The shape of a is (1, 3)
The shape of b is (3, 1)
回顧進行廣播機制的兩步
- 檢查數組 a 和 b 形狀是否兼容,從兩個形狀元組 (1, 3) 和 (3, 1)最後一個元素開始檢查,發現它們都滿足『有一個等於 1』的條件。
- 因此它們形狀兼容,兩個數組的最終形狀爲 (max(1,3), max(3,1)) = (3, 3)
到此,a 和 b 被擴展成 (3, 3) 的數組,讓我們看看 a + b 等於多少?
c = a + b
print('The shape of c is', c.shape)
print('a is', a)
print('b is', b)
print('c = a + b =', c)
The shape of c is (3, 3)
a is [[1 2 3]]
b is [[4]
[5]
[6]]
c = a + b = [[5 6 7]
[6 7 8]
[7 8 9]]
例二:維度不一樣
a = np.arange(5)
b = np.array(2)
print('The dimension of a is', a.ndim, 'and the shape of a is', a.shape)
print('The dimension of b is', b.ndim, 'and the shape of b is', b.shape)
The dimension of a is 1 and the shape of a is (5,)
The dimension of b is 0 and the shape of b is ()
數組 a 和 b 形狀分別爲 (5,) 和 (),首先我們把缺失的維度用 1 補齊得到 (5,) 和 (1,),再根據廣播機制那套流程得到這兩個形狀是兼容的,而且最終形狀爲 (5,)。
c = a + b
print('The dimension of c is', c.ndim, 'and the shape of c is', c.shape, '\n')
print('a is', a)
print('b is', b)
print('c = a + b =', c)
The dimension of c is 1 and the shape of c is (5,)
a is [0 1 2 3 4]
b is 2
c = a + b = [2 3 4 5 6]
例五:
a = np.array([[[1,2,3], [4,5,6]]])
b1 = np.array([[1,1,1], [2,2,2], [3,3,3]])
b2 = np.arange(3).reshape((1, 3))
b3 = np.arange(6).reshape((2, 3))
b4 = np.arange(12).reshape((2, 2, 3))
b5 = np.arange(6).reshape((2, 1, 3))
print('a的維度:', a.ndim, 'a的形狀:', a.shape)
print('b1的維度:', b1.ndim, 'b1的形狀:', b1.shape)
print('b2的維度:', b2.ndim, 'b2的形狀:', b2.shape)
print('b3的維度:', b3.ndim, 'b3的形狀:', b3.shape)
print('b4的維度:', b4.ndim, 'b4的形狀:', b4.shape)
print('b5的維度:', b5.ndim, 'b5的形狀:', b5.shape)
a的維度: 3 a的形狀: (1, 2, 3)
b1的維度: 2 b1的形狀: (3, 3)
b2的維度: 2 b2的形狀: (1, 3)
b3的維度: 2 b3的形狀: (2, 3)
b4的維度: 3 b4的形狀: (2, 2, 3)
b5的維度: 3 b5的形狀: (2, 1, 3)
對於數組 a 和 b1,它們形狀是 (1, 2, 3) 和 (3, 3)。元組最後一個都是 3,兼容;倒數第二個是 3 和 2,即不相等,也沒有一個是 1,不兼容!a 和 b1 不能進行廣播機制。不行就看看下面代碼:
c1 = a + b1
print(c1)
print(c1.shape)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-159-224e02cbb1e0> in <module>
----> 1 c1 = a + b1
2 print(c1)
3 print(c1.shape)
ValueError: operands could not be broadcast together with shapes (1,2,3) (3,3)
a 和其他 b2, b3, b4, b5 都可以進行廣播機制。
c2 = a + b2
print(c2)
print(c2.shape)
c3 = a + b3
print(c3)
print(c3.shape)
c4 = a + b4
print(c4)
print(c4.shape)
c5 = a + b5
print(c5)
print(c5.shape)
[[[1 3 5]
[4 6 8]]]
(1, 2, 3)
[[[ 1 3 5]
[ 7 9 11]]]
(1, 2, 3)
[[[ 1 3 5]
[ 7 9 11]]
[[ 7 9 11]
[13 15 17]]]
(2, 2, 3)
[[[ 1 3 5]
[ 4 6 8]]
[[ 4 6 8]
[ 7 9 11]]]
(2, 2, 3)