[机器学习实战札记] NumPy基础

时间:2021-03-02 21:24:06

<<机器学习实战>>一书非常注重实践,对每个算法的实现和使用示例都提供了python实现。在阅读代码的过程中,发现对NumPy有一定的了解有助于理解代码。特别是NumPy中的数组和矩阵,对于初次使用者而言,有点难以理解。下面就总结一下NumPy基础知识。

NumPy和SciPy、Scikit-learn、pandas等库一样,是数据科学领域不可或缺的库,它提供了比python list更好的数组数据结构:更紧凑、读写速度更快、更加方便和高效。NumpPy包含两种基本的数据类型:数组和矩阵,二者在处理上稍有不同。

NumPy数组

NumPy数据处理

与标准的python不同,使用NumPy处理数组中的数据可以省去循环语句。比如:

>>> import numpy as np
>>> mm = np.array([1, 1, 1])
>>> pp = np.array([1, 2, 3])
>>> mm + pp
array([2, 3, 4])

不需要循环,就像两个平常的两个数,做加法就可以完成数组的相加。

另外还有一些操作,在NumPy中能够简单的完成,比如在每个元素上乘以常量2,可以写成:

>>> pp * 2
array([2, 4, 6])

对每个元素平方,这在K-近邻算法中用到:

>>> pp ** 2
array([1, 4, 9])

两个数组相乘,即两个数组(数组维度必须相同)的元素对应相乘:

>>> a1 = np.array([1, 2, 3])
>>> a2 = np.array([0.3, 0.2, 0.3])
>>> a1 * a2
array([0.3, 0.4, 0.9])

NumPy数组属性

一个numpy数组是一个由不同数值组成的网格,网格中的数据都是同一种数据类型,可以通过非负整型数的元组来访问。

  • data 代表数组第一个字节的内存地址
  • dtype 描述元素的数据类型
  • shape 描述数组维度上的大小,它是一个元组,即使是一维数组
  • strides 描述从一数组元素到下一数组元素在内存中要前进的字节数,它也是一个元组,strides值与数据类型和数组维度大小有关。比如strides值为(10, 1)意味着前进1个字节获得下一列的数据,而前进10个字节定位到下一行数据。
>>> my_2d_array = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype=np.int64)
>>> print(my_2d_array.data)
<memory at 0x7fdd8c0e4480>
>>> print(my_2d_array.shape)
(2, 4)
>>> print(my_2d_array.dtype)
int64
>>> print(my_2d_array.strides)
(32, 8)

NumPy中也支持多维数组,多维数组中的元素也可以像列表中一样访问:

>>> jj = np.array([[1, 2, 3], [1, 1, 1]])
>>> jj[0]
array([1, 2, 3])
>>> jj[0][1]
2

也可以用矩阵方式访问:

>>> jj[0, 1]
2

创建数组

我们可以从列表,通过np.array()函数创建数组,然后利用方括号访问其中的元素,array()函数还可以增加一个可选的参数,指明数组元素的数据类型:

import numpy as np

a = np.array([1,2,3,4], dtype=np.int64)
print a

b = np.array([[1,2,3],[4,5,6]])   # Create a rank 2 array
print b                           
print b.shape                     # Prints "(2, 3)"
print b[0, 0], b[0, 1], b[1, 0]   # Prints "1 2 4"

结果:

[1 2 3 4]
[[1 2 3] [4 5 6]]
(2, 3)
1 2 4

Numpy还提供了很多其他创建数组的方法:

import numpy as np

a = np.zeros((2,2))  # Create an array of all zeros
print a              # Prints "[[ 0. 0.]
                     # [ 0. 0.]]"

b = np.ones((1,2))   # Create an array of all ones
print b              # Prints "[[ 1. 1.]]"

c = np.full((2,2), 7) # Create a constant array
print c               # Prints "[[ 7. 7.]
                      # [ 7. 7.]]"

d = np.eye(2)        # Create a 2x2 identity matrix
print d              # Prints "[[ 1. 0.]
                     # [ 0. 1.]]"

e = np.random.random((2,2)) # Create an array filled with random values
print e                     # Might print "[[ 0.91940167 0.08143941]
                            # [ 0.68744134 0.87236687]]"

注意,np.eye()和np.identity()函数用于创建单位矩阵,所谓单位矩阵,就是矩阵的主对角线上的元素都为1, 而其它元素都为0,矩阵和单位矩阵相乘,积不变。

在<<机器学习实战>>中还使用到了np.tile函数,其定义如下:

numpy.tile(A, reps)

重复reps次A,形成一个数组。这里reps可以是数字,也可以是元组。示例如下:

>>> a = np.array([0, 1, 2])
>>> np.tile(a, 2)
array([0, 1, 2, 0, 1, 2])
>>> np.tile(a, (2, 2))
array([[0, 1, 2, 0, 1, 2], [0, 1, 2, 0, 1, 2]])
>>> np.tile(a, (2, 1, 2))
array([[[0, 1, 2, 0, 1, 2]],
       [[0, 1, 2, 0, 1, 2]]])

注意多维数组,经过多维的扩充:

>>> b = np.array([[1, 2], [3, 4]])
>>> np.tile(b, 2)
array([[1, 2, 1, 2], [3, 4, 3, 4]])
>>> np.tile(b, (2, 1))
array([[1, 2], [3, 4], [1, 2], [3, 4]])

是不是结果有点出乎意料,多尝试一下就可以理解其中的扩充逻辑了。

NumPy矩阵

矩阵的关键字matrix也可以简写为mat:

>>> ss = np.mat([1, 2, 3])
>>> ss
matrix([[1, 2, 3]])
>>> mm = np.matrix([1, 2, 3])
>>> mm
matrix([[1, 2, 3]])

访问矩阵中的单个元素:

>>> mm[0, 1]
2

注意矩阵的乘法含义,比如1x3的矩阵是不能与1x3的矩阵相乘的。比如ss乘以mm,会出现如下错误,必须将其中一个转置:

>>> mm * ss
Traceback (most recent call last):
  File "/home/alex/anaconda3/lib/python3.6/site-packages/IPython/core/interactiveshell.py", line 2910, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-50-7262af0f2071>", line 1, in <module>
    mm * ss
  File "/home/alex/anaconda3/lib/python3.6/site-packages/numpy/matrixlib/defmatrix.py", line 309, in __mul__
    return N.dot(self, asmatrix(other))
ValueError: shapes (1,3) and (1,3) not aligned: 3 (dim 1) != 1 (dim 0)

>>> mm * ss.T
matrix([[14]])

其中.T方法完成了ss的转置。

要实现数组那样的相乘,可以通过multiply函数实现:

>>> np.multiply(mm, ss)
matrix([[1, 4, 9]])

排序

sort()方法用于排序,在原地进行,这意味着排序后的结果占用原始的存储空间,如果希望保留数据的原序,必须事先做一份拷贝。

argsort()方法得到矩阵中每个元素的排序序号:

>>> dd = np.mat([4, 5, 1])
>>> dd.argsort()
matrix([[2, 0, 1]])
>>> dd.sort()
>>> dd
matrix([[4, 5, 1]])

索引

对于矩阵,如果想取出其中一行的元素,可以使用:操作符和行号来完成:

>>> jj = np.mat([[1, 2, 3], [8, 8, 8]])
>>> jj[1, :]
matrix([[8, 8, 8]])

也可以指定范围:

>>> jj[1, 0:2]
matrix([[8, 8]])

注意,这个范围是前闭后开,比如0:2并不包括索引值为2的元素,索引值也可以超过该维度的大小,比如:

>>> jj[1, 0:3]
matrix([[8, 8, 8]])
>>> jj[1, 0:4]
matrix([[8, 8, 8]])

参考

  1. Python Numpy Array Tutorial
  2. CS231n课程笔记翻译:Python Numpy教程
  3. <<机器学习实战>>,p298 ~ p301
  4. NumPy v1.13 Manual

[机器学习实战札记] NumPy基础