1. 程式人生 > >Python 機器學習基礎(二)——Numpy 篇

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 傳播的規則是,把低維陣列通過複製轉化為與高維陣列同維度,再進行兩兩運算。如下圖所示:

Numpy array broadcasting

為陣列增加一個維度:

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