Python 机器学习基础(二)——Numpy

时间:2022-10-21 21:24:35

本文是 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 传播的规则是,把低维数组通过复制转化为与高维数组同维度,再进行两两运算。如下图所示:

Python 机器学习基础(二)——Numpy

为数组增加一个维度:

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