Python 資料科學基本上包含三部分 :
- Numpy
- Pandas
- Matplotlib
Python 內建資料型態 list 屬於動態型態 (C 語言的 struct 結構), 資料結構中含有指標群指向元素物件, 但可儲存異質資料這優點的代價是, 若使用 list 儲存同質性 (固定型態) 的數值資料, 在資料存取與運算上速度太慢缺乏效率. 雖然 Python 3.3 之後內建了 array 模組, 但它只支援一維陣列且陣列運算函數不多, 故 Python 內建資料型態均不適合用來做高維度陣列運算, 參考 :
# Python 學習筆記 : 陣列模組 array 測試
Jim Hugunin 與 Travis Oliphant (Anaconda 的創辦人與 Scipy 開發者) 分別在 1996 年與 2005 年相繼開發了 Python 的開源數學運算擴充函式庫 Numeric 與 Numarray, 後來於 2006 年結合發展成 Numpy 套件. Numpy 採用 BLAS 與 LAPACK 線性代數函式庫 API, 透過底層以 C 與 Fortran 撰寫的程式碼來提升高維向量與陣列運算效能, 其函數介面與功能與 MATLAB 非常類似, 參考 :
# 維基 : Numpy
Numpy 套件以 ndarray 物件為核心, 由資料本身與元資料 (metadata, 陣列維度與型別 dtype 等) 資訊構成, 其資料均為同質性 (homogeneous, 即元素的資料型別都一樣), 每一個元素在記憶體中都是緊密配置在連續的記憶體空間, 因此存取元素的效率比串列高; 此外, Numpy 提供了大量以 C 語言撰寫的 ufunc 函數 (universal function) 以進行高效運算.
Numpy 是第三方套件, 必須先安裝才能使用, 安裝指令如下 :
pip3 install numpy
如果要做科學計算, 則可以直接安裝 Scipy, 它會自動安裝 Numpy, 因為 Scipy 是以 Numpy 為基礎而建立的 :
pip3 install scipy
Numpy 的官方文件參考 :
# https://numpy.org/devdocs/user/index.html
# https://numpy.org/devdocs/reference/index.html
# https://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html
本篇測試範例參考了如下書籍 :
# Python 程式設計經典 (吳昱禎, 碁峰)
# Python 資料運算與分析實戰 (中久喜見司, 旗標)
# Python 資料科學與人工智慧 (陳允傑, 旗標)
# Python 資料科學學習手冊 (何敏煌譯, 歐萊里)
# Deep learning from scratch (齋藤康毅, 歐萊里)
# Python 在大數據科學計算上的最佳實作 (張若愚, 博碩)
一. 載入 Numpy 套件 :
Numpy 是第三方套件, 因此使用前須載入, 通常會取 np 當作別名 :
import numpy as np
這樣就可以用別名 np 作為名稱空間呼叫 numpy 的函數, 例如查詢版本或用 help() 查詢 Numpy 使用說明 :
numpy 查詢指令 | 說明 |
np.__version__ | 查詢 Numpy 版本 |
np.info(np) | 查詢 Numpy 訊息 |
help(np) | 查詢 Numpy 使用說明, 可查詢函數用法例如 np.arange |
np.lookfo('函數名') | 查詢函數用法文件 |
>>> import numpy as np
>>> np.__version__ #查詢 Numpy 版本
'1.16.0'
二. 建立陣列 :
陣列是一種資料結構, 用來儲存具有相同資料型態的有序 (ordered) 元素, 這些元素都是配置在連續的記憶體中. 在資料科學中, 一維的數值陣列稱為向量 (vector); 二維數值陣列稱為矩陣 (matrix); 三維以上的數值陣列則稱為張量 (tensor). 如以張量作為數值序列的一般化型態名稱, 向量可稱為一維張量; 矩陣是二維張量, 三維陣列則是三維張量.
最常用的陣列是二維的矩陣, EXCEL 試算表就是矩陣結構. 對於矩陣而言, 其維度 i*j 表示 "列*行", 意即此二維陣列元素有 i 列 j 行 (注意, 台灣習慣是橫列直行, 但在看強國人出版的簡體書時要注意, 他們的用法是直列橫行), 在 Python 與 Numpy 中, 陣列的索引都是 0 起始的.
通常使用元組來表示陣列的形狀 (shape), 例如形狀為 (2, 3, 4) 的陣列表示這是一個三維陣列, 它有三個軸 (axis), 依序是軸 0, 軸 1, 與軸 3 (axis 也是 0 起始的), 長度分別是 2, 3, 4.
Numpy 提供如下常用函數可用來建立陣列, 這些函數都是 Numpy 的全域方法 (即類別方法), 呼叫這些函數都會傳回一個 numpy.ndarray 陣列物件 :
np 建立陣列方法 | 說明 |
array(串列或元組) | 傳入串列或元組來建立陣列, 傳回 numpy.ndarray 物件 |
arange(e) | 建立元素值為 0, 1, 2 ... (b-1) 的陣列 |
arange(b, e) | 建立元素值為 b, b+1, b+2 ... (e-1) 的陣列 |
arange(b, e, s) | 建立元素值為 b, b+s, b+2s ... (e-1) 的陣列 (步階為 s) |
linspace(b, e) | 建立 50 個等差數列元素之陣列 (含 e), 公差為 (e-b)/49 |
linspace(b, e, n) | 建立 n 個等差數列元素之陣列 (含 e), 公差為 (e-b)/(n-1) |
logspace(b, e) | 建立 50 個基底為 10 等比數列元素之陣列 (含 e) |
logspace(b, e, n) | 建立 n 個基底為 10 等比數列元素之陣列 (含 e) |
logspace(b, e, n, base=k) | 建立 n 個基底為 k 等比數列元素之陣列 (含 e) |
zeros(n) | 建立 n 個元素的一維零陣列 (全部元素為 0) |
zeros((i, j)) | 建立 i*j 維 (i 列 j 行) 的零陣列 (全部元素為 0) |
ones(n) | 建立 n 個 元素的一維壹陣列 (全部元素為 1) |
ones((i, j)) | 建立 i*j 維 (i 列 j 行) 的壹陣列 (全部元素為 1) |
full(n, k) | 建立 n 個元素的一維陣列 (全部元素為 k) |
full((i, j), k) | 建立 i*j 維 (i 列 j 行) 的陣列 (全部元素為 k) |
zeros_like(a) | 建立與範本陣列 a 結構一樣的零陣列 (全部元素為 0) |
ones_like(a) | 建立與範本陣列 a 結構一樣的壹陣列 (全部元素為 1) |
identity(n) | 建立 n*n 維 (n 列 n 行) 的對角矩陣 (對角元素為 1, 其餘為 0) |
eye(n) | 建立 n*n 維 (n 列 n 行) 的對角矩陣 (對角元素為 1, 其餘為 0) |
eye(n, k) | 建立 n*n 維 (n 列 n 行) 的對角矩陣 (第 k 條對角元素為 1, 其餘為 0) |
diag(v, k) | v=二維傳回 v 指定對角線向量; v=一維建立以 v 為對角線之方陣 |
empty(n) | 建立 n 個元素的未初始化一維陣列 |
empty((i, j)) | 建立 i*j 維 (i 列 j 行) 的未初始化陣列 |
random.rand() | 傳回 0~1 之間的一個隨機浮點數 (純量) |
random.rand(n) | 建立 n 個元素的一維隨機陣列 (均勻分布隨機浮點數) |
random.rand(i, j) | 建立 i*j 維 (i 列 j 行) 的隨機陣列 (均勻分布隨機浮點數) |
random.randint(L, H, size) | 建立 [L, H] 間形狀為 size (元組) 的隨機陣列 (常態分佈隨機整數) |
random.randn(n) | 建立 n 個元素的一維常態分布隨機陣列 (常態分佈隨機浮點數) |
random.random((i, j)) | 同 rand() 建立 i*j 維 (i 列 j 行) 的隨機陣列 (均勻分布隨機浮點數) |
fromfunction(func, shape) | 依據指定之外形 shape 執行函數 func 建立陣列 |
其中 array(), arange(), linspace(), 用來建立特定元素值的陣列; random 模組的 rand() 等函式用來建立不特定 (隨機) 陣列, 其餘函數則用來建立預設內容之陣列.
上表中的函數都有一個備選參數 dtype 可用來指定元素之數值資料型態, 每一個函數有個別之預設值 :
dtype | 說明 |
bool_ | 布林值 (byte) |
int_ | 預設之整數型態 (int32 或 int64) |
intc | C 語言之 int (int32 或 int64) |
intp | 做指標用之整數 (int32 或 int64) |
int8 | 有號 8 位元整數 (值 -128 至 127) |
int16 | 有號 16 位元整數 (值 -32768 至 32767) |
int32 | 有號 32 位元整數 (值 -2147483648 至 2147483647) |
int64 | 有號 64 位元整數 (值 -9223372036854775808 至 9223372036854775807) |
uint8 | 無號 8 位元整數 (值 0 至 255) |
uint16 | 無號 16 位元整數 (值 0 至 65535) |
uint32 | 無號 32 位元整數 (值 0 至 429497295) |
uint64 | 無號 64 位元整數 (值 0 至 18446744073709551615) |
float_ | 同 float64 |
float16 | 16 位元浮點數 (半精度), 含符號位元 1, 指數位元 5, 小數 10 位元 |
float32 | 32 位元浮點數 (單精度), 含符號位元 1, 指數位元 8, 小數 23 位元 |
float64 | 64 位元浮點數 (倍精度), 含符號位元 1, 指數位元 11, 小數 52 位元 |
complex_ | 同 complex128 |
complex64 | 64 位元複數 |
complext128 | 128 位元複數 |
dtype 是上面每一個建立陣列的函數都有的備選參數, 其設定方式可直接指定, 也可用字串, 也可以用匿名 np 存取 :
- dtype=float64
- dtype='float64'
- dtype=np.float64
ndarray 物件常用屬性 | 說明 |
dtype | 陣列元素之資料型態, 例如 int32/int64/float64 等 |
shape | 陣列的形狀, 型態為 tuple, 例如 (2, 3, 4) |
ndim | 陣列的軸數或維 (D) 度 |
size | 陣列的元素總數 |
itemsize | 每個元素所佔之 bytes 數 |
nbytes | 整個陣列物件所佔之 bytes 數=itemsize*size |
查詢形狀 (內部結構) 除了可用物件的 shape 屬性外, 也可以呼叫全域方法 numpy.shape(a) 函數, 只要將陣列當參數傳進去即可.
陣列的形狀可以呼叫物件方法 reshape() 並傳入一個元組來改變, 它會傳回一個新陣列參考, 但此新陣列與原陣列元素共用記憶體空間, 新陣列只是陣列外觀 (view) 不同而已, 元素仍然是儲存在原來的記憶體位址不變, 因此若更改新陣列的元素值連帶舊陣列元素也會改變.
1. 從元組或串列字面值建立陣列 :
呼叫 np.array() 可建立一維或多維陣列, 其介面如下 :
numpy.array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0)
可見至少必須傳入一個物件參數, 通常是元組或串列字面值, array() 會將其轉換成 numpy.ndarray 物件, 例如 :
>>> a=np.array([1,2,3]) #將串列轉成一維陣列
>>> type(a)
<class 'numpy.ndarray'> #陣列維 numpy.ndarray 型別
>>> print(a) #顯示陣列 a 內容
array([1, 2, 3])
>>> b=np.array((1,2,3)) #將元組轉成一維陣列
>>> type(b)
<class 'numpy.ndarray'>
>>> print(b) #顯示陣列 b 內容
array([1, 2, 3])
>>> a == b #依序比較陣列元素之值
array([ True, True, True])
>>> c=np.array([[1,2,3],[4,5,6]]) #將二維串列轉成二維陣列
>>> type(c)
<class 'numpy.ndarray'>
>>> print(c) #顯示陣列 b 內容
array([[1, 2, 3],
[4, 5, 6]])
>>> np.array(range(10)) #呼叫 range() 產生串列再轉成陣列
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.array(range(1,10)) #呼叫 range() 產生串列再轉成陣列
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.array([range(i, i+3) for i in (2, 4, 6)]) #使用串列生成式產生串列再轉成陣列
array([[2, 3, 4],
[4, 5, 6],
[6, 7, 8]])
接著檢視 ndarray 物件的屬性 :
>>> a=np.array([1,2,3]) #一維整數陣列
>>> a.dtype #元素型態為 int32
dtype('int32')
>>> a.shape #形狀為 1*3
(3,)
>>> np.shape(a) #查詢形狀也可呼叫 numpy.shape() 傳入陣列
(3,)
>>> a.ndim #維度為一維
1
>>> a.size #有 3 個元素
3
>>> a.itemsize #每個元素佔 4 bytes (4*8=32)
4
>>> a.nbytes #整個陣列佔 3*4=12 bytes
12
呼叫陣列物件的 reshape() 方法並傳入一個元組會傳回指定形狀之陣列, 但這是原陣列的視圖 (view), 傳回的參考仍指向原陣列的記憶體空間 (即其元素與原陣列是共用記憶體), 兩者只是外觀不同而已, 因此改變傳回陣列之元素也會改變原陣列內容 :
>>> a=np.array(range(12)) #建立 1*12 的一維陣列
>>> print(a)
[ 0 1 2 3 4 5 6 7 8 9 10 11]
>>> b=a.reshape((3, 4)) #改變形狀為 3*4
>>> print(b)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
>>> b[0][0]=1 #改變 b 的第 0 元素
>>> print(b) #b[0][0] 變 1 了
[[ 1 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
>>> print(a) #a[0][0] 也變 1 了
[ 1 1 2 3 4 5 6 7 8 9 10 11]
Numpy 提供了 may_share_memory() 函數來檢驗兩個 ndarray 物件是否共用記憶體, 例如上面從陣列 a 變形為 b, 兩者共用記憶體, 呼叫 may_share_memory(a, b) 會傳回 True :
>>> a=np.array([1,2,3]) #一維整數陣列
>>> a.dtype #元素型態為 int32
dtype('int32')
>>> a.shape #形狀為 1*3
(3,)
>>> np.shape(a) #查詢形狀也可呼叫 numpy.shape() 傳入陣列
(3,)
>>> a.ndim #維度為一維
1
>>> a.size #有 3 個元素
3
>>> a.itemsize #每個元素佔 4 bytes (4*8=32)
4
>>> a.nbytes #整個陣列佔 3*4=12 bytes
12
呼叫陣列物件的 reshape() 方法並傳入一個元組會傳回指定形狀之陣列, 但這是原陣列的視圖 (view), 傳回的參考仍指向原陣列的記憶體空間 (即其元素與原陣列是共用記憶體), 兩者只是外觀不同而已, 因此改變傳回陣列之元素也會改變原陣列內容 :
>>> a=np.array(range(12)) #建立 1*12 的一維陣列
>>> print(a)
[ 0 1 2 3 4 5 6 7 8 9 10 11]
>>> b=a.reshape((3, 4)) #改變形狀為 3*4
>>> print(b)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
>>> b[0][0]=1 #改變 b 的第 0 元素
>>> print(b) #b[0][0] 變 1 了
[[ 1 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
>>> print(a) #a[0][0] 也變 1 了
[ 1 1 2 3 4 5 6 7 8 9 10 11]
Numpy 提供了 may_share_memory() 函數來檢驗兩個 ndarray 物件是否共用記憶體, 例如上面從陣列 a 變形為 b, 兩者共用記憶體, 呼叫 may_share_memory(a, b) 會傳回 True :
>>> a=np.array(range(12))
>>> b=a.reshape((3, 4))
>>> np.may_share_memory(a, b) # a, b 陣列共用記憶體
True
注意, 傳入 array() 的串列或元組結構必須一致 (即元素長度必須相同), 否則會變成結構性陣列, 其元素仍然是串列, 而非陣列元素, 例如 :
>>> a1=np.array([[1,2,3],[4,5,6]])
>>> print(a1)
[[1 2 3]
[4 5 6]]
>>> a2=np.array([[1,2,3],[4,5]])
>>> print(a2)
[list([1, 2, 3]) list([4, 5])]
上面 a2 陣列的第二個串列少了 1 個元素, 因此 Numpy 會以串列物件儲存, 而不是全部轉成陣列.
2. 建立等差數列之陣列 :
呼叫 arange() 與 linspace() 這兩個函數都可建立以等差數列為元素的一維陣列, 兩者的差別是 linspace() 預設有包含結束值, 而 arange() 則不包含, 即產生半開數列 [b, e); 其次是傳入三個參數時 arange(b, e, s) 第三個參數 s 是步階值 (即公差), 而 linspace(b, e, n) 的第三參數 n 則是指元素個數.
(1). arrange() 函數 :
函數 arrange() 是 Python 內建函數 range() 的 Numpy 陣列版, 它可傳入 1~3 個參數, 當只有一個參數時 arange(e) 會建立一維陣列, 其元素為 0~e (但不包含 e) 步階為 1 的等差數列 (即公差=1), 介面如下 :
numpy.arange([start, ]stop, [step, ]dtype=None)
例如 :
numpy.arange([start, ]stop, [step, ]dtype=None)
例如 :
>>> np.arange(10) #建立 [0, 10) 步階為 1 的一維陣列
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
如果傳入兩個參數, arange(b, e) 會建立一維陣列, 其元素為 b~e (但不包含 e) 步階為 1 的等差數列, 例如 :
>>> np.arange(2, 10) #建立 [2, 10) 步階為 1 的一維陣列
array([2, 3, 4, 5, 6, 7, 8, 9])
如果傳入三個參數, arange(b, e, s) 會建立一維陣列, 其元素為 b~e 但不包含 e, 步階為 s 的等差數列, 例如 :
>>> np.arange(1, 10, 2) #建立 [1, 10) 步階為 2 的一維陣列
array([1, 3, 5, 7, 9])
三個參數也可以是浮點數, 例如 :
>>> np.arange(1.2, 5.6, 0.8)
array([1.2, 2. , 2.8, 3.6, 4.4, 5.2])
>>> np.arange(1.2, 2.4, 0.2)
array([1.2, 1.4, 1.6, 1.8, 2. , 2.2]) #不含結束值 2.4
注意, 雖然在起訖是整數情況下, arrange() 所產生的等差數列不包含結束的 end 數值, 但若 end 是浮點數時因為捨入誤差的關係, 產生的有可能會包含 end 參數, 例如 :
>>> np.arange(0.1, 0.4, 0.1) # 注意 0.4 有被包含在數列中
array([0.1, 0.2, 0.3, 0.4])
>>> np.arange(0.1, 0.5, 0.1)
array([0.1, 0.2, 0.3, 0.4])
>>> np.arange(0.1, 0.6, 0.1)
array([0.1, 0.2, 0.3, 0.4, 0.5])
>>> np.arange(1, 1.3, 0.1) # 注意 1.3 有被包含在數列中
array([1. , 1.1, 1.2, 1.3])
參考 :
(2). linespace() 函數 :
函數 linspace() 也可以建立元素是等差數列的一維陣列, 它至少需傳入兩個參數, 分別為開始值與結束值, linspace(b, e) 會建立一個 50 個元素的一維陣列, 其元素為範圍 b~e (包含 e), 步階或公差為 (e-b)/49 的等差數列, 介面如下 :
numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
例如 :
>>> np.linspace(0, 10) #建立 [0, 10] 步階為 10/49 的一維陣列
array([ 0. , 0.20408163, 0.40816327, 0.6122449 , 0.81632653,
1.02040816, 1.2244898 , 1.42857143, 1.63265306, 1.83673469,
2.04081633, 2.24489796, 2.44897959, 2.65306122, 2.85714286,
3.06122449, 3.26530612, 3.46938776, 3.67346939, 3.87755102,
4.08163265, 4.28571429, 4.48979592, 4.69387755, 4.89795918,
5.10204082, 5.30612245, 5.51020408, 5.71428571, 5.91836735,
6.12244898, 6.32653061, 6.53061224, 6.73469388, 6.93877551,
7.14285714, 7.34693878, 7.55102041, 7.75510204, 7.95918367,
8.16326531, 8.36734694, 8.57142857, 8.7755102 , 8.97959184,
9.18367347, 9.3877551 , 9.59183673, 9.79591837, 10. ])
因為預設是 50 個元素, 中間有 49 段區間, 故步階值為 (10-0)/49=0.20408163.
如果傳入三個參數 linspace(b, e, n), 則第三參數 n 為元素個數, 它會將此 b~e 區段均分成 (n-1) 段 以產生元素, 因此步階值或公差為 (e-b)/(n-1), 例如 :
>>> np.linspace(0, 10, 9) #[0~10] 分 8 段, 步階=10/8=1.25
array([ 0. , 1.25, 2.5 , 3.75, 5. , 6.25, 7.5 , 8.75, 10. ])
>>> np.linspace(0, 10, 10) #[0~10] 分 9 段, 步階=10/9=1.1111
array([ 0. , 1.11111111, 2.22222222, 3.33333333, 4.44444444,
5.55555556, 6.66666667, 7.77777778, 8.88888889, 10. ])
>>> np.linspace(0, 10, 11) #[0~10] 分 10 段, 步階=10/10=1
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
3. 建立等比數列之陣列 :
呼叫 numpy.logspace() 可建立元素為等比數列的陣列, 其介面如下 :
numpy.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0)[source]
可見 logspace() 至少須傳入兩個參數 (整數, 浮點數, 或複數均可), 注意, 這兩個參數是指數, 不是數列的首項與末項, 預設基底為 10, 等比數列起始值為 base**start (即 base 的 start 次方), 結束值為 base**stop, 預設會建立 50 個元素的等比數列陣列 (含結束值). 例如 np(0, 2) 會建立 [0, 100] 之間 50 個等比數列元素之陣列 :
>>> np.logspace(0, 2) #建立 0~100 間 50 個等比數列元素之陣列
array([ 1. , 1.09854114, 1.20679264, 1.32571137,
1.45634848, 1.59985872, 1.75751062, 1.93069773,
2.12095089, 2.32995181, 2.55954792, 2.8117687 ,
3.0888436 , 3.39322177, 3.72759372, 4.09491506,
4.49843267, 4.94171336, 5.42867544, 5.96362332,
6.55128557, 7.19685673, 7.90604321, 8.68511374,
9.54095476, 10.48113134, 11.51395399, 12.64855217,
13.89495494, 15.26417967, 16.76832937, 18.42069969,
20.23589648, 22.22996483, 24.42053095, 26.82695795,
29.47051703, 32.37457543, 35.56480306, 39.06939937,
42.9193426 , 47.14866363, 51.79474679, 56.89866029,
62.50551925, 68.6648845 , 75.43120063, 82.86427729,
91.0298178 , 100.])
可見 np.logspace(0, 2) 的公比為 1.09854114, 依據等比數列公式 :
np.logspace(0, 2) 的首項為 10**0=1, 末項為 10**2=100, 因此公比為末項除以首項後再開 49 次方根, 即 (100/1)**(1/49)=1.09854114.
可傳入第三參數 num 指定元素個數, 例如 :
>>> np.logspace(0, 2, 10) #建立 [1, 100] 10 個元素等比陣列
array([ 1. , 1.66810054, 2.7825594 , 4.64158883,
7.74263683, 12.91549665, 21.5443469 , 35.93813664,
59.94842503, 100. ])
首項與末項同樣是 1 與 100, 但元素為 10 個, 故公比要開 9 次方為 (100/1)**(1/9)=1.66810054.
可傳入 base 參數指定基底, 例如 :
>>> np.logspace(0, 2, 10, base=2) #建立基底=10 的等比陣列
array([1. , 1.16652904, 1.36079 , 1.58740105, 1.85174942,
2.16011948, 2.5198421 , 2.93946898, 3.42897593, 4.])
因基底為 2, 因此首項為 2**0=1, 末項=2**2=4, 所以公比為 (4/1)**(1/9)=1.16652904.
4. 建立預設內容陣列 :
呼叫 zeros(), ones(), full(), zeros_like(), ones_like(), eye(), diag(), identity() 等函數可建立預設內容之陣列, 例如零陣列, 壹陣列, 與對角陣列等, 它們都可以傳入 dtype 參數指定資料型態.
(1). 零陣列 :
zeros() 必須傳入一個參數, 當傳入一個正整數時, zeros(n) 表示要建立有 n 個元素的一維零陣列 (所有元素為 0, 若 n=0 則建立空陣列); 而傳入一個元組時 zeros((i, j)) 表示要建立一個 i 列 j 行的零陣列, 例如 :
>>> np.zeros(5) #建立 5 個元素的一維零陣列
array([0., 0., 0., 0., 0.])
>>> np.zeros(5, dtype=int) #指定元素資料型態為 int
array([0, 0, 0, 0, 0])
>>> np.zeros((2, 3)) #建立 i 列 j 行的零陣列
array([[0., 0., 0.],
[0., 0., 0.]])
>>> np.zeros(0) #建立空陣列 (預設資料型態為 float64)
array([], dtype=float64)
zeros_like() 可依據傳入之陣列結構, 建立與其維度相同之零陣列, 例如 :
>>> a=np.array([[1, 2 , 3], [4, 5 ,6]]) #建立一個 2*3 陣列
>>> print(a)
[[1 2 3]
[4 5 6]]
>>> b=np.zeros_like(a) #依據 a 陣列結構建立零陣列
>>> print(b)
[[0 0 0]
[0 0 0]]
>>> b.dtype #zeros_like() 建立之陣列預設型態為整數
dtype('int32')
可見 zeros() 建立元素之預設型態為 float 浮點數, 而 zeros_like() 則是整數.
(2). 壹陣列 :
ones() 與 zeros() 一樣必須傳入一個參數, 當傳入一個正整數時, ones(n) 表示要建立有 n 個元素的一維壹陣列 (所有元素為 1, 若 n=0 建立空陣列); 而傳入一個元組時 ones((i, j)) 表示要建立一個 i 列 j 行的壹陣列, 例如 :
>>> np.ones(5) #建立 5 個元素的一維壹陣列
array([1., 1., 1., 1., 1.])
>>> np.ones(5, dtype=int) #指定元素資料型態為 int
array([1, 1, 1, 1, 1])
>>> np.ones((2, 3)) #建立 i 列 j 行的壹陣列
array([[1., 1., 1.],
[1., 1., 1.]])
>>> np.ones(0) #建立空陣列
array([], dtype=float64)
ones_like() 可依據傳入之陣列結構, 建立與其維度相同之壹陣列, 例如 :
>>> a=np.array([[1, 2 , 3], [4, 5 ,6]]) #建立一個 2*3 陣列
>>> print(a)
[[1 2 3]
[4 5 6]]
>>> b=np.ones_like(a) #依據 a 陣列結構建立壹陣列
>>> print(b)
[[1 1 1]
[1 1 1]]
>>> b.dtype #ones_like() 建立之陣列預設型態為整數
dtype('int32')
(4). 填滿陣列 :
呼叫 numpy.full() 可建立元素全為指定值的填滿陣列, 其介面為 :
numpy.full(shape, fill_value, dtype=None, order='C')
第一個參數為陣列外形, 可傳入一個正整數, 元組或串列; 第二個參數是要填入的元素值. 當第一參數是正整數時, full(n, k) 會建立一個具有 n 個元素值為 k 的一維陣列; 當第一參數是元組或串列時, full(shape, k) 會依據外形 shape 建立元素值為 k 的高維陣列, 例如 :
>>> np.full(5, 3.14159) #用 pi 填滿一維陣列
array([3.14159, 3.14159, 3.14159, 3.14159, 3.14159])
>>> np.full((2, 3), 3.14159) #用 pi 填滿二維陣列
array([[3.14159, 3.14159, 3.14159],
[3.14159, 3.14159, 3.14159]])
>>> np.full([2, 3], 3.14159) #用 pi 填滿二維陣列
array([[3.14159, 3.14159, 3.14159],
[3.14159, 3.14159, 3.14159]])
>>> np.full([2, 3, 4], 9) #用 9 填滿三維陣列
array([[[9, 9, 9, 9],
[9, 9, 9, 9],
[9, 9, 9, 9]],
[[9, 9, 9, 9],
[9, 9, 9, 9],
[9, 9, 9, 9]]])
(5). 對角陣列 (矩陣) :
呼叫 identity() 會建立一個中央對角線元素值為 1 的對角方陣, 介面如下 :
numpy.identity(n, dtype=None)
至少須傳入一個正整數參數以建立一個 n*n 方陣 :
>>> np.identity(4) #建立 4*4 對角方陣 (浮點數)
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
>>> np.identity(4, dtype=int) #指定型態為整數
array([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]])
numpy.eye(n, m=None, k=0, dtype=np.float, order='C')
第一參數 n 為矩陣的列數, 第二參數 m 為矩陣的行數, 第三參數 k 為元素值為 1 之對角線編號, 預設 0 為中央正對角線, k 為正整數時, 由中央往右上角第 k 條對角線元素全部為 1, 其餘為 0; k 為負整數時, 由中央往左下角第 k 條對角線元素全部為 1, 其餘為 0, 例如 :
>>> np.eye(4) #建立 4*4 單位方陣
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
>>> np.eye(4, k=1) #建立 4*4 對角矩陣
array([[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.],
[0., 0., 0., 0.]])
>>> np.eye(4, k=-1) #左下方第 1 條對角線填 1
array([[0., 0., 0., 0.],
[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.]])
>>> np.eye(4, k=2) #右上方第 2 條對角線填 1
array([[0., 0., 1., 0.],
[0., 0., 0., 1.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> np.eye(4, k=3) #右上方第 3 條對角線填 1
array([[0., 0., 0., 1.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> np.eye(4, k=4) #右上方第 4 條對角線填 1 (超過了)
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> np.eye(4, 3) #4*3 矩陣 (非方陣)
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.],
[0., 0., 0.]])
可見當 k 超過對角線總數時會變成建立零陣列.
另外一個與對角陣列有關的函數是 diag(), 其介面如下 :
numpy.diag(v, k=0)
第一個函數是一維或二維陣列, 第二個參數與 eye() 中的 k 參數一樣, 中央對角線 k 為 0 (預設值), 往右上走之對角線依序為 1, 2, ... ; 往左下走之對角線依序為 -1, -2, ... 等等. 若第一參數 v 傳入一個二維陣列, 則以一維陣列形式將其第 k 的對角線元素傳回 (getter); 若 v 傳入一個一維陣列, 則以該陣列元素取代第 k 個對角線.
首先建立一個 5*5 方陣如下 :
>>> a=np.array(range(25)) #建立 [0, 1, 2, .... , 24] 一維陣列
>>> x=a.reshape((5,5)) #調整形狀建立 5*5 方陣
>>> print(x)
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, 24]])
將此二維陣列傳給 diag() 會傳回其第 k 的對角線元素 :
>>> np.diag(x) #傳回方陣 x 的中央對角線
array([ 0, 6, 12, 18, 24]) #傳回值為一維陣列
>>> np.diag(x, k=1) #傳回方陣 x 的 k=1 對角線
array([ 1, 7, 13, 19])
>>> np.diag(x, k=-1) #傳回方陣 x 的 k=-1 對角線
array([ 5, 11, 17, 23])
如果傳入一維陣列, 則會傳回一個指定對角線是該一維陣列的方陣, 例如 :
>>> np.diag([1, 2, 3, 4, 5]) #建立指定中央對角線之方陣
array([[1, 0, 0, 0, 0],
[0, 2, 0, 0, 0],
[0, 0, 3, 0, 0],
[0, 0, 0, 4, 0],
[0, 0, 0, 0, 5]])
>>> np.diag([1, 2, 3, 4, 5], k=1) #k=1 右上對角線方陣
array([[0, 1, 0, 0, 0, 0],
[0, 0, 2, 0, 0, 0],
[0, 0, 0, 3, 0, 0],
[0, 0, 0, 0, 4, 0],
[0, 0, 0, 0, 0, 5],
[0, 0, 0, 0, 0, 0]])
>>> np.diag([1, 2, 3, 4, 5], k=-1) #k=-1 左下對角線方陣
array([[0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0],
[0, 2, 0, 0, 0, 0],
[0, 0, 3, 0, 0, 0],
[0, 0, 0, 4, 0, 0],
[0, 0, 0, 0, 5, 0]])
(6). 空陣列 :
呼叫 empty() 會建立一個指定形狀的未初始化陣列, 其元素值為所配置記憶體內原本的值, 其介面如下 :
numpy.empty(shape, dtype=float, order='C')
第一參數為陣列外形, 可以是正整數, 或元素為正整數之元組或串列 (通常用元組), 例如 :
>>> np.empty(4) #建立 4 個元素的未初始化一維陣列
array([1., 1., 1., 1.])
>>> np.empty((4,)) #建立 4 個元素的未初始化一維陣列
array([1., 1., 1., 1.])
>>> np.empty((2, 3)) #建立 2*3 未初始化二維陣列
array([[3.14159, 3.14159, 3.14159],
[3.14159, 3.14159, 3.14159]])
>>> np.empty((2, 3, 4)) #建立 2*3*4 未初始化三維陣列
array([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
可見傳入 (4, ) 相當於傳入整數 4, 都是建立一維陣列.
5. 建立隨機內容之陣列 :
Numpy 底下的 random 模組提供了幾個方法可用來建立隨機陣列, 不過如果要讓隨機數可預測, 可以透過呼叫 np.random.seed() 設定隨機種子, 這樣在同一隨機種子下重複呼叫時, 傳回的隨機數列值與順序是一樣的 :
np.random.seed(i)
傳入參數 i 須為正整數.
(1). rand() 與 random() 函數 :
此函數會傳回 [0, 1) 區間均勻分布隨機陣列, 介面如下 :
numpy.random.rand(d0, d1, ..., dn)
rand() 的參數必須是正整數, 都是可選的, 未傳入參數時會傳回一個 0~1 之間的均勻分布隨機浮點數 (純量); 傳入一個參數 n (須正整數) 時 random.rand(n) 會傳回 n 個元素的一維均勻分布隨機陣列, 這相當於 random.rand(1, n); 傳入兩個參數 (須正整數) 時 random.rand(i, j) 會傳回一個 i*j 維度的均勻分布隨機隨機陣列, 更多參數則產生更高維的陣列, random.rand(i, j, k) 會傳回三維陣列, 例如 :
>>> np.random.rand() #傳回 0~1 間的隨機數 (float 純量)
0.19644093345544167
>>> type(np.random.rand()) #沒有傳入參數時傳回一個純量
<class 'float'>
>>> np.random.rand(5) #傳回 1*5 一維隨機陣列
array([0.74998027, 0.78622133, 0.92885681, 0.18321391, 0.89496996])
>>> np.random.rand(3, 4) #傳回 3*4 二維隨機陣列
array([[0.27912439, 0.73034461, 0.58826309, 0.59707398],
[0.44257555, 0.01745328, 0.59587659, 0.68253437],
[0.22404294, 0.19438719, 0.99749655, 0.49556471]])
>>> np.random.rand(2, 3, 4) #傳回 2*3*4 三維隨機陣列
array([[[0.27131017, 0.40616805, 0.36128135, 0.75666407],
[0.40549157, 0.41149331, 0.30765576, 0.39208383],
[0.82069598, 0.60894839, 0.4239132 , 0.92491804]],
[[0.38630791, 0.36035005, 0.56409681, 0.15552691],
[0.74267144, 0.81580992, 0.8991233 , 0.34849928],
[0.37512254, 0.56358772, 0.60626021, 0.11272833]]])
可見 random.rand() 的元素值都是介於 0~1 之間.
另外 random.random() 功能與 random.rand() 一樣, 差別只是 random.random() 之參數為表示陣列形狀的物件, 通常是元組, 其介面如下 :
numpy.random.randdom(shape)
例如 :
>>> np.random.random((2, 3, 4)) #傳回 2*3*4 三維隨機陣列
array([[[0.8636309 , 0.85336527, 0.17550813, 0.11426248],
[0.84716226, 0.7543792 , 0.24974123, 0.67595818],
[0.81296995, 0.5708457 , 0.35645387, 0.46249463]],
[[0.86340479, 0.20525588, 0.86448148, 0.67061865],
[0.78973599, 0.87887531, 0.85816169, 0.37024847],
[0.06263618, 0.10129474, 0.7355295 , 0.30408336]]])
參考 :
# np.random.rand vs np.random.random
(2). randn() 函數 :
此函數與 rand() 一樣會傳回 [0, 1) 區間指定形狀之隨機陣列, 不同之處為 randn() 的隨機數為常態分布 (結尾的 n 表示 normal distribution), 而 rand() 為均勻分布, 介面如下 :
numpy.random.rand(d0, d1, ..., dn)
例如 :
>>> np.random.randn() #傳回 0~1 間的隨機數 (float 純量)
0.3856502495158207
>>> np.random.randn(5) #傳回 1*5 一維隨機陣列
array([ 0.1719701 , 0.30347725, -1.62440603, -0.2287934 , 0.8188689 ])
>>> np.random.randn(3, 4) #傳回 3*4 二維隨機陣列
array([[ 0.42688578, 0.39268864, -0.3210757 , -0.59786833],
[ 0.62772359, 0.95096534, 0.47403366, 1.07871119],
[-0.46120767, 0.02107573, -1.12275642, -0.44598128]])
>>> np.random.randn(2, 3, 4) #傳回 2*3*4 三維隨機陣列
array([[[ 0.65388739, -1.57062685, 1.29388902, -0.10002825],
[-1.34442224, -1.8373028 , 0.68837754, 0.13592657],
[-0.1309182 , -1.41535012, 0.2141069 , 1.30175303]],
[[ 1.83388743, 0.18719303, -1.58136535, 0.69444909],
[-1.20114114, -1.32826911, 0.16630603, -1.48900367],
[ 0.77561497, 0.18556015, 0.12806819, -0.70700624]]])
(3). randint() 函數 :
顧名思義, 此函數名結尾的 int 表示它會建立一個隨機整數陣列, 介面如下 :
numpy.random.randint(low, high=None, size=None, dtype='l')
可見 randint() 至少必須傳入一個參數 low, 表示傳回的隨機數最小值, 第二參數 high 為傳回的隨機數最大值 (不含), 第三參數為陣列維度, 可用元組或串列指定 (通常用元組), 例如 size=(2, 3) 表示建立 2*3 維度陣列, 預設傳回值型態為整數. 資料型態預設為 32 位元整數 'I', 也可以用其它整數型態例如 64 位元整數 'i8', 但不可指定為非整數型態.
總之, 此函數會傳回一個元素值大於等於 low, 小於 high, 維度為 size 的均勻分布隨機數, 但實際測試發現, 當只傳入一個參數時, 它會被視為是 high 而不是 low, 例如 randint(4) 會傳回 [0, 4) 間隨機數, 必須同時傳入兩個參數時才會被解讀為前一個 low, 後一個 high, 例如 :
>>> np.random.randint(4) #傳回小於 4 的隨機數
2
>>> np.random.randint(low=4) #傳回小於 4 的隨機數
1
>>> np.random.randint(1, 4) #傳回 [1, 4) 間的隨機數
3
>>> np.random.randint(2, 31) #傳回 [2, 31) 間的隨機數
21
>>> np.random.randint(2,10, [2,3]) #傳回 [2, 10) 間的隨機陣列
array([[5, 4, 8],
[2, 3, 9]])
參考 :
# 为什么你用不好Numpy的random函数?
6. 呼叫函數建立陣列 :
呼叫 numpy.fromfunction() 可透過函數呼叫來建立陣列, 介面如下 :
numpy.fromfunction(function, shape, **kwargs)
第一參數為建立陣列要呼叫的函數 (可用 lamda), 第二參數為輸入陣列的外形 (也是輸出陣列的外形), 通常是正整數元組 (tuple). 注意, shape 並非傳入函數 function() 之參數, 傳入函數的參數是符合此外形的座標陣列 (coordinate array), 例如 shape=(2, 2) 時, 傳入參數為 [[0, 0], [1, 1]] 與 [[0, 1], [0, 1]] 這兩個陣列, 例如 :
>>> def func(i, j): #呼叫的函數傳回兩陣列加 1 之乘積
... return (i+1)*(j+1)
...
>>> np.fromfunction(func, (2, 2))
array([[1., 2.],
[2., 4.]])
此處傳入函數 func(i, j) 的參數 i, j 分別為 [[0, 0], [1, 1]] 與 [[0, 1], [0, 1]] 這兩個陣列, 所以傳回 i+1=[[1, 1], [2, 2]] 與 j+1=[[1, 2], [1, 2]] 的陣列乘積為 [[1, 2], [2, 4]], 此可由下列陣列乘積印證 :
>>> i=np.array([[1, 1], [2, 2]]) #二維陣列
>>> j=np.array([[1, 2], [1, 2]]) #二維陣列
>>> i*j
array([[1, 2],
[2, 4]])
與上面呼叫 fromfunction() 結果是一樣的. 如果傳入外形 shape=(9, 9) 就可以建立九九乘法表陣列, 例如 :
>>> def func(i, j):
... return (i+1)*(j+1)
...
>>> np.fromfunction(func, (9, 9))
array([[ 1., 2., 3., 4., 5., 6., 7., 8., 9.],
[ 2., 4., 6., 8., 10., 12., 14., 16., 18.],
[ 3., 6., 9., 12., 15., 18., 21., 24., 27.],
[ 4., 8., 12., 16., 20., 24., 28., 32., 36.],
[ 5., 10., 15., 20., 25., 30., 35., 40., 45.],
[ 6., 12., 18., 24., 30., 36., 42., 48., 54.],
[ 7., 14., 21., 28., 35., 42., 49., 56., 63.],
[ 8., 16., 24., 32., 40., 48., 56., 64., 72.],
[ 9., 18., 27., 36., 45., 54., 63., 72., 81.]]
參考 :
# Numpy里的fromfunction()的问题?
# Python資料分析(三)NumPy
沒有留言 :
張貼留言