资源 | 从数组到矩阵的迹,NumPy常见使用大总结

选自Hackernoon

作者:Rakshith Vasudev

机器之心编译

参与:蒋思源

本文为初学者简要介绍了 NumPy 库的使用与规则,通过该科学计算库,我们能构建更加高效的数值计算方法。此外,因为机器学习存在着大量的矩阵运算,所以 NumPy 允许我们在 Python 上实现高效的模型。

NumPy 是 Python 语言的一个扩充程序库。支持高效的多数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。NumPy 的科学计算十分高效,因此弥补了 Python 在运算效率上的不足。

在本文中,我们将简单介绍在机器学习和数据科学中应用最广的科学计算库,可以说它的高效令使用 Python 开发机器学习算法成为了可能。此外,我们也常认为正是因为 NumPy,Python 才可以像 MATLAB 那样高效地执行矩阵运算。

以下将开启我们的 NumPy 之旅:

import numpy as np

如上在 Python 内导入 NumPy 库,「np」简写即我们调用 NumPy 时约定俗成的命名。下面,我们分别创建了一个 Python 数组和 NumPy 数组:

# python array
a = [1,2,3,4,5,6,7,8,9]
# numpy array
A = np.array([1,2,3,4,5,6,7,8,9])

以下分别打印了这两个变量的值与类型:

print(a)
print(A)
print(type(a))
print(type(A))
====================================================================
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1 2 3 4 5 6 7 8 9]
<class 'list'>
<class 'numpy.ndarray'>

那么我们为什么要使用 NumPy 数组而不使用标准的 Python 数组呢?原因可能是 NumPy 数组远比标准数组紧密,在使用同样单精度变量下,NumPy 数组所需要的内存较小。此外,NumPy 数组是执行更快数值计算的优秀容器。

np.arange()

下面是另一种定义数组元素的方式:

np.arange(0,10,2)
====================================================================
array([0, 2, 4, 6, 8])

其中 arange([start],stop,[step]) 声明了该数组元素起始与终止的值,而 step 定义了给定区间内采样的步幅大小。在以上代码中,我们生成一个从零开始到 10 结束(不包含 10),并且每次加 2 的数组。注意数组元素取值服从左闭右开原则,即取 0 而不取 10,停止数值并不能取到。

下面是另一个案例:

np.arange(2,29,5)
====================================================================
array([ 2,  7, 12, 17, 22, 27])

其实在 NumPy 中的数组可以等价的称之为矩阵或向量。所以当我们称矩阵的维度是 2×3 时,这并没有错误,我们同样还是在描述一个多维数组。如下展示了一个 2×3 阶矩阵:

array([ 2,  7, 12,], 
      [17, 22, 27])

现在我们可以讨论默认 NumPy 数组的形状(shape),即等同于讨论矩阵的维度。形状是 np 数组一个非常重要的属性,下面使用 shape 方法调用变量 A 的形状:

A = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) 
A.shape
====================================================================
(9,)

这是一个秩为 1 的矩阵,因此我们看到输出的形状只有一个元素。我们可以使用 reshape() 函数将该数组转化为我们想要的维度,如下,我们将 B 的形状转化为 3×3,reshape() 方法将会返回一个多维数组,因此它的左右分别有两个方括号。

因为 Python 定义的列表没有 reshape() 方法,该博客给出的标准数组会报错。我们只能对 NumPy 数组执行 reshape。此外,执行 reshape 方法要求转化前和转化后的元素数量是一样的。

B = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) 
B.reshape(3,3)
====================================================================
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

我们可以如下输出 B 的形状:

B.shape
====================================================================
(3,3)

np.zeros()

下面,我们可以使用 np.zero() 函数生成一个元素全是零的矩阵。如下在给定需要生成矩阵的形状后,其就能自动填充零值:

np.zeros((4,3))
====================================================================
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])

np.zeros((n,m)) 将返回一个 n*m 阶矩阵,其中每个值都为零。

np.eye()

eye() 方法将生成一个单位矩阵:

np.eye(5)
====================================================================
array([[ 1.,  0.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.,  0.],
       [ 0.,  0.,  1.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  0.,  1.]])

np.eye(n) 将生成一个 n 阶单位方阵,即一个 n 阶矩阵,其主对角线元素都为 1,其它元素都为 0。

np.dot()

矩阵乘法在机器学习中十分重要,以下展示了怎样使用 NumPy 执行矩阵乘法。我们一般使用 np.dot() 执行矩阵乘法,即点积。执行该乘法的前提是左边矩阵的列数(每行的元素)必须等于右边矩阵的行数,否则就会报错。此外,根据矩阵乘法的定义,左乘和右乘也不一样,这一点我们需要注意。

若 A=(2,3),而 B=(3,2),那么 A 左乘 B 就要求 A 的列数 3 等于 B 的函数 3。下面展示了 NumPy 矩阵乘法:

# generate an identity matrix of (3 x 3)
I = np.eye(3)
I
====================================================================
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
# generate another (3 x 3) matrix to be multiplied.
D = np.arange(1,10).reshape(3,3)
D
====================================================================
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

以上定义了两个矩阵,下面则执行矩阵 D 左乘矩阵 I:

# perform actual dot product.
M = np.dot(D,I)
M
====================================================================
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.],
       [ 7.,  8.,  9.]])

np.sum()

np.sum() 会将整个矩阵的所有元素加和为一个标量值:

# add all the elements of matrix.
sum_val = np.sum(M)
sum_val
====================================================================
45.0

此外,我们还可以提供参数以确定到底是沿矩阵的行累加还是沿矩阵的列累加。如下我们给定参数 axis=1,其代表将每一行的元素累加为一个标量值。

# sum along the rows
np.sum(M,axis=1)
====================================================================
array([  6.,  15.,  24.])

第一行累加为 6、第二行累加为 15、第三行累加为 24。此外,给定参数 axis=0 则表示沿列累加:

# sum along the cols
np.sum(M,axis=0)
====================================================================
array([ 12.,  15.,  18.])

第一列累加为 12、第二列累加为 15、第三列累加为 18。

np.random.rand()

我们可以使用 np.random.rand() 随机生成矩阵,即给定矩阵的形状,其中每个元素都是随机生成的。如下随机生成了一个 2×2 矩阵:

# generate random values in a 2 x 3 matrix form
np.random.rand(2,3)
====================================================================
array([[ 0.2248368 ,  0.49652272,  0.76189091],
       [ 0.73520939,  0.48107188,  0.3883801 ]])

当然我们也能扩展到随机生成更高维度的矩阵:

# generate random values in a 12 x 13 matrix form
np.random.rand(12,13)
====================================================================
array([[ 0.43385691,  0.15503296,  0.19860119,  0.65346609,  0.16774261,0.56058978,  0.84974275,  0.05887681,  0.27276929,  0.88750259,0.25141674,  0.05663906,  0.54186252],
       [ 0.2635477 ,  0.88291404,  0.42043263,  0.83565607,  0.92982761,0.79879409,  0.91323242,  0.37954769,  0.60198588,  0.44773903,0.70699903,  0.3892703 ,  0.94314732],
       [ 0.12593268,  0.97838364,  0.81297353,  0.3368167 ,  0.33501746,0.99619471,  0.22476839,  0.93321408,  0.41301684,  0.01808732,0.61321647,  0.22462791,  0.468457  ],
       [ 0.63765001,  0.13884884,  0.67648642,  0.65589694,  0.80931411,0.46202022,  0.40819602,  0.03863341,  0.16494124,  0.69603883,0.96849077,  0.19150476,  0.8968954 ],
       [ 0.25646945,  0.21928867,  0.70952192,  0.80569537,  0.84562245,0.54595757,  0.00684613,  0.19142737,  0.94387805,  0.80871064,0.73648968,  0.80105002,  0.16716087],
       [ 0.3894393 ,  0.61933361,  0.41088568,  0.88781578,  0.40932049,0.90947387,  0.71984125,  0.81259019,  0.69020009,  0.56480145,0.43041522,  0.02650665,  0.7738148 ],
       [ 0.21326808,  0.2036178 ,  0.30368209,  0.51081501,  0.64345557,0.99061654,  0.96805793,  0.19446453,  0.25974565,  0.74033622,0.37379014,  0.67444828,  0.82899251],
       [ 0.47571066,  0.82012796,  0.50881338,  0.3900192 ,  0.34356749,0.36440024,  0.58048805,  0.74650051,  0.24974157,  0.70129048,0.99920892,  0.29142188,  0.09263266],
       [ 0.4140815 ,  0.25578684,  0.5485647 ,  0.07581615,  0.28539059,0.93805043,  0.56897052,  0.23606972,  0.78568646,  0.609795,0.70741831,  0.51003452,  0.53791667],
       [ 0.53967367,  0.78513565,  0.94739241,  0.03891731,  0.15962705,0.45470422,  0.56172944,  0.49735169,  0.35216862,  0.87391629,0.43953245,  0.18160601,  0.78307107],
       [ 0.1725005 ,  0.89132449,  0.05287284,  0.2113003 ,  0.69802999,0.12609322,  0.83490382,  0.34199806,  0.90740966,  0.33934554,0.02015816,  0.13498658,  0.06695927],
       [ 0.14066135,  0.34828447,  0.0780561 ,  0.00126867,  0.57958087,0.93641585,  0.70294758,  0.21712057,  0.24902555,  0.53284372,0.19795993,  0.69817631,  0.71156616]])

np.append()

如果我们需要手动地给一个数组添加一个或多个元素,那么我们可以使用 np.append()。

# generate an array using np.arange()
A = np.arange(5,15,2)
A
====================================================================
array([ 5,  7,  9, 11, 13])

下面使用 np.append() 添加一个元素到数组 A 中:

A = np.append(A,19)
A
====================================================================
array([ 5,  7,  9, 11, 13, 19])

np.append() 同样可以将一个具体的数组添加到已有的数组中:

A = np.append(A,[3,55,34,553])
A
====================================================================
array([  5,   7,   9,  11,  13,  19,   3,  55,  34, 553])

如上我们将一个列表及其元素添加到了 np 数组中。

np.diff()

若给定一个数组,我们该如何求取该数组两个元素之间的差?NumPy 提供了 np.diff() 方法以求 A[n+1]-A[n] 的值,该方法将输出一个由所有差分组成的数组。

A=np.array([5,   7,   9,  11,  13,  19,   3,  55,  34, 553])
B = np.diff(A,n=1)
B
====================================================================
array([  2,   2,   2,   2,   6, -16,  52, -21, 519])

我们需要注意执行差分运算后的数组要比原数组的元素少 1 位。其中 n=1 代表执行一次求差分,并返回差分的数组。而 n=2 代表执行两次差分,并返回第二次求差分后的数组。第二次求差分是在第一次差分结果数组上进行的。如下对 A 求两次差分等价于对上文 B 再求一次差分。

# parameter n indicates that this diff() must be run twice. 
np.diff(A,n=2)
===================================================================
array([  0,   0,   0,   4, -22,  68, -73, 540])

np.vstack() 和 np.column_stack()

若我们希望将多个向量或矩阵按一定的方法堆叠成新的矩阵,那么 np.vstack() 和 np.column_stack() 方法将帮助我们实现这一操作。以下定义三个行向量:

# lets define 3 lists.
a = [1,2,3]
b = [4,5,6]
c = [7,8,9]

堆叠一共有两种变体,即按行堆叠还是按列堆叠。按行堆叠即将需要的向量或矩阵作为新矩阵的一个行,按列堆叠即一个向量作为新矩阵的一列。以下展示了 np.vstack((a,b,c)) 如何将向量 a、b、c 分别作为新矩阵的第一行、第二行和第三行:

# directly stack with lists passed in the same order.
np.vstack((a,b,c))
===================================================================
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
np.vstack((b,a,c))
===================================================================
array([[4, 5, 6],
       [1, 2, 3],
       [7, 8, 9]])

此外,np.column_stack() 可以将每个元素作为一列,例如 np.column_stack((a,b,c)) 就将向量 a 作为第一列、b 作为第二列、c 作为第三列:

np.column_stack((a,b,c))
===================================================================
array([[1, 4, 7],
       [2, 5, 8],
       [3, 6, 9]])
np.column_stack((b,a,c))
===================================================================
array([[4, 1, 7],
       [5, 2, 8],
       [6, 3, 9]])

np 数组索引

NumPy 数组的索引方式和 Python 列表的索引方式是一样的,从零索引数组的第一个元素开始我们可以通过序号索引数组的所有元素。例如 A[i] 索引数组 A 中的第 i+1 个元素。此外,我们还能索引一串元素:

A=np.array([5,   7,   9,  11,  13,  19,   3,  55,  34, 553])
A[2:5]  
===================================================================
array([ 9, 11, 13])

如上 A[2:5] 索引了数组 A 中第 3 到第 5 个元素,注意 Python 列表和数组的索引都是左闭右开,即 A 中包含 2 索引的元素而不包含 5 索引的元素:

A[lowerbound(inclusive): upperbound(exclusive)]

广播操作

广播操作是 NumPy 非常重要的一个特点,它允许 NumPy 扩展矩阵间的运算。例如它会隐式地把一个数组的异常维度调整到与另一个算子相匹配的维度以实现维度兼容。所以将一个维度为 [3,2] 的矩阵与一个维度为 [3,1] 的矩阵相加是合法的,NumPy 会自动将第二个矩阵扩展到等同的维度。

为了定义两个形状是否是可兼容的,NumPy 从最后开始往前逐个比较它们的维度大小。在这个过程中,如果两者的对应维度相同,或者其一(或者全是)等于 1,则继续进行比较,直到最前面的维度。若不满足这两个条件,程序就会报错。

如下展示了一个广播操作:

a = np.array([1.0,2.0,3.0,4.0, 5.0, 6.0]).reshape(3,2)

b = np.array([3.0])
a * b
====================================================================

array([[  3.,   6.],
       [  9.,  12.],
       [ 15.,  18.]])

严格数学意义上,a 和 b 是不能执行矩阵乘法的,因为它们的维度不符合要求。但在 NumPy 的广播机制下,维度为 1 的项何以扩展到相应的维度,所以它们就能够执行运算。

矩阵的运算

以下执行了矩阵的转置操作:

a = np.array([[1,0],[2,3]])
>>> print a
[[1 0]
 [2 3]]
>>> print a.transpose()
[[1 2]
 [0 3]]

运算矩阵的迹:

>>> print np.trace(a)
4

此外,numpy.linalg 模块中有很多关于矩阵运算的方法,如下据算矩阵的特征值与特征向量:

>>> import numpy.linalg as nplg
>>> print nplg.eig(a)
(array([ 3.,  1.]), array([[ 0.        ,  0.70710678],
       [ 1.        , -0.70710678]]))

原文地址:https://hackernoon.com/@rakshithvasudev

本文为机器之心编译,转载请联系本公众号获得授权。

原文发布于微信公众号 - 机器之心(almosthuman2014)

原文发表时间:2017-10-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习从入门到成神

稀疏矩阵转置

矩阵是线性代数中的一个知识,刚开始学习的时候可能感觉不到它有什么用处,最初的感觉就是对二维数据的操作。其实现实生活中矩阵的用处太大了,设计领域相当的广泛。在此只...

30310
来自专栏calmound

hust 1607 Triangles(经典好题)

题意:给出一定的点,然后再给出哪些点相连,问最后这些相连点的组成多少个三角形,hash的应用 分析:转载牛人的思想 题意:给一些点的坐标和里面的点构成的一些线段...

30480
来自专栏Python小屋

Python+numpy实现函数向量化

Python本身对向量操作的支持并不是很好,需要借助列表推导式或函数式编程来实现,例如: >>> import random # 生成随机测试数据 >>> x...

90150
来自专栏深度学习计算机视觉

打印字符图形(蓝桥杯基础题 Java版)

问题描述 利用字母可以组成一些美丽的图形,下面给出了一个例子: ABCDEFG BABCDEF CBABCDE DCBABCD EDCBABC 这...

46360
来自专栏和蔼的张星的图像处理专栏

730. 所有子集的和递归

给一整数 n, 我们需要求前n个自然数形成的集合的所有可能子集中所有元素的和 样例

15520
来自专栏chenjx85的技术专栏

leetcode-896-单调数列

如果对于所有 i <= j,A[i] <= A[j],那么数组 A 是单调递增的。 如果对于所有 i <= j,A[i]> = A[j],那么数组 A 是单调递...

14230
来自专栏向治洪

数据结构之2-3-4树

2-3-4树是一种阶为4的B树。它是一种自平衡的数据结构,可以在O(lgn)的时间内查找、插入和删除,这里的n是树中元素的数目。2-3-4树和红黑树是等价的,也...

19290
来自专栏窗户

有限域(2)——理想和商环

  我们上一节介绍了环(ring)、域(field)的概念,并给了一些环、域的实例。比如我们知道整数环、方阵环、有理数域、实数域等。我们知道,域是环的一个种。最...

10220
来自专栏CDA数据分析师

Python之numpy数组学习(二)

前言 前面我们学习了numpy库的简单应用,今天来学习下比较重要的如何处理数组。 处理数组形状 下面可将多维数组转换成一维数组时的情形。 利用以下函数处理数组的...

31980
来自专栏函数式编程语言及工具

泛函编程(28)-粗俗浅解:Functor, Applicative, Monad

    经过了一段时间的泛函编程讨论,始终没能实实在在的明确到底泛函编程有什么区别和特点;我是指在现实编程的情况下所谓的泛函编程到底如何特别。我们已经习惯了传统...

23460

扫码关注云+社区

领取腾讯云代金券