Python 機器學習基礎(二)——Numpy 篇
本文是 Python 機器學習基礎系列文章的第二篇——Numpy 篇。
Numpy
Numpy 是 Python 的一種開源數值計算擴充套件包,它可以用於儲存和處理大型矩陣,比 Python 自帶的巢狀列表結構要高效得多。
Numpy 陣列(Numpy array)
陣列(array)是 numpy 模組的一個主要類,可以表示向量(一維)、矩陣(二維)或高維陣列,如聲音、影象、視訊等,並可以進行面向向量或矩陣的運算。
建立 array
用 array 建構函式建立一個 numpy 陣列,可以是 1-D, 2-D, 3-D,…:
import numpy as np
a = np.array([0 , 1, 2, 3])
print a.ndim, a.shape
b = np.array([[0, 1, 2], [3, 4, 5]])
print b.ndim, b.shape
c = np.array([[[1], [2]], [[3], [4]]])
print c.ndim, c.shape
其中 array() 函式的輸入是一個 list 結構。
可以呼叫函式建立一些特殊的 array:
a = np.arange(10) # 序列陣列
b = np.arange(1, 9, 2) # start, end (exclusive), step
c = np.linspace(0 , 1, 6) # start, end, num-points
d = np.linspace(0, 1, 5, endpoint=False)
e = np.ones((3, 3)) # 用 tuple 作為輸入
f = np.zeros((2, 2))
g = np.eye(3)
h = np.diag(np.array([1, 2, 3, 4]))
m = np.random.rand(4)
b = np.random.randn(4)
np.random.seed(1234) # 隨機數種子
總結:建立 array 時,資料用 list 指定,維度用 tuple 指定。
基本資料型別
Numpy 中的基本資料型別包括:bool, int8~int64/int, uint8~uint64/uint, float16~float64/float, complex64, complex128/complex, S7(字串)等。使用 a.dtype
可以檢視陣列的元素資料型別,在建立陣列時指定 dtype
關鍵字可以指定資料型別。
a = np.array([1, 2, 3])
b = np.array([1, 2, 3], dtype=float)
c = np.array([1., 2., 3.])
d = np.array([1+2j, 3+4j, 5+6*1j])
e = np.array(['Bonjour', 'Hello', 'Hi'])
print a.dtype, b.dtype, c.dtype, d.dtype, e.type
np.array
建立的陣列預設型別是 int64
,其他函式建立的 array 預設是 float
型別。可以使用 astype()
方法轉換型別,如 b = a.astype('float')
。
索引和切片
array 的索引和切片與 Python 的序列容器(list, tuple 等)幾乎完全一致,同樣使用索引符 []
來索引,從 0 開始索引,冒號符 a[start:end:step]
來切片,a[::-1]
可以翻轉一個一維陣列。
a = np.arange(10)
print a[0], a[2], a[-1], a[::-1]
對於多維陣列,可以使用逗號分隔的多個索引來取值:
a = np.diag(np.arange(3))
print a, a[1], a[1,1], a[0,2], a[:2, ::-1]
b = np.arange(10)
c = np.arange(5)
b[5:] = c[::-1]
需要注意的是,與 Python 序列容器不同,array 上的索引和切片只是原資料的一個檢視(view)或引用,而非拷貝。因此對索引或切片的任何更改都會反映到原始資料上。如果需要複製,用 array 的 copy 方法。此外,可以用 np.may_share_memory
函式檢查兩個變數是否共享記憶體。
a = np.arange(10)
print a
b = a[::2]
b[0] = 12
print b, a
c = a[::2].copy()
c[0] = 12
print c, a
print np.may_share_memory(a, b) # True
print np.may_share_memory(a, c) # False
用陣列索引
Numpy arrays 可以用數字或 slice 索引,但也可以使用布林(boolean)或整數(integer)陣列來索引(作為 mask)。這類索引叫 fancy indexing。
# 使用等維度 bool array 索引
np.random.seed(3)
a = np.random.random_integers(0, 20, 15) # [0,20] is the scope, 15 is num-element
b = a[a % 3 == 0]
print a, b
mask = np.array([1,0,1,0,0, 0,0,0,1,1, 1,1,0,1,1], dtype=bool)
a[mask] = 0
print a
# 使用整數 list 或 array 索引
a = np.arange(0, 100, 10)
print a[[2, 3, 2, 4, 2]]
a[[9, 7]] = -1
print a
idx = np.array([[3, 4], [9, 7]])
print a, a[idx]
Array 上的數值運算
點對點運算
Array 上所有的算術運算均為點對點(elementwise)運算。如下例項:
a = np.array([1, 2, 3, 4])
print a + 1, 2**a, 2^(3*a) - a
b = np.ones(4) + 1
print a - b, a + b
c = np.ones((3, 3))
print c * c
要想執行矩陣乘法,用 dot()
方法:
c = np.ones((3, 3))
print c * c, c.dot(c)
陣列比較運算等:
a = np.array([1, 2, 3, 4])
b = np.array([4, 2, 2, 4])
print a == b, a > b # 點對點比較,輸出布林陣列
print np.array_equal(a, b) # 陣列比較,輸出單個布林值
a = np.array([1, 1, 0, 0])
b = np.array([1, 0, 1, 0])
print np.logical_or(a, b)
print np.logical_and(a, b)
print np.sin(a), np.log(a), np.exp(a)
a = np.triu(np.ones((3, 3)), 1)
print a, a.T, a + a.T
此外,np.allclose(a, b) 用於逐元素判斷陣列 a, b 的值是否足夠接近(小於指定 tolerance)。
陣列統計運算
包括求和、最大最小值、中值、平均值、標準差等。
x = np.array([[1, 1], [2, 2]])
print x.sum(axis=0), x[:,0].sum(), x[1,:].sum()
print x.min(), x.max(), x.argmax(), x.argmin()
print x.mean(), np.median(x), x.std()
a = np.zeros((100, 100))
print np.any(a != 0), np.all(a == a)
數值運算的傳播
上面講的點對點運算只針對等維度的陣列。但是,我們同樣可以在維度不等的陣列之間進行數值運算,Numpy 會自動將它們轉換為維度相等的陣列。這個過程叫做數值運算的傳播(broadcasting)。
a = np.tile(np.arange(0, 40, 10), (3, 1)).T
b = np.array([0, 1, 2])
print a + b
a = np.ones((4, 5))
a[0] = 2
array 傳播的規則是,把低維陣列通過複製轉化為與高維陣列同維度,再進行兩兩運算。如下圖所示:
為陣列增加一個維度:
a = np.arange(0, 40, 10)
print a.shape # (4,)
a = a[:, np.newaxis]
print a.shape # (4, 1)
b = np.array([0, 1, 2])
print a + b
陣列變形
拉成向量(ravel 函式)與 reshape:
a = np.array([[1, 2, 3], [4, 5, 6]])
print a.ravel(), a.T, a.T.ravel()
b = a.revel().reshape((2, 3))
print b
高維資料會先將最後一個維度拉平。增加一個維度:
z = np.array([1, 2, 3])
print z
print z[:, np.newaxis], z[np.newaxis, :]
維度重新排序(transpose 函式):
a = np.arange(4*3*2).reshape(4, 3, 2)
print a.shape
b = a.transpose(1, 2, 0)
print b.shape
resize 與 reshape 不一樣,resize 可以更改維度,並在預設的地方補 0:
a = np.arange(4)
a.resize((8,))
print a # 0, 1, 2, 3, 0, 0, 0, 0
但是,當資料被其他變數名引用時是不能 resize 的:
b = a
a.resize((4,)) # 報錯
陣列相關函式
排序:
a = np.array([[4, 3, 5], [1, 2, 1]])
b = a.sort(axis=1) # 分別對每行排序
j = np.argsort(a)
print a, a[j]
j_max = np.argmax(a)
j_min = np.argmin(a)
print j_max, j_min