Appearance
NumPy的应用-4
向量
向量(vector)也叫矢量,是一个同时具有大小和方向,且满足平行四边形法则的几何对象。与向量相对的概念叫标量或数量,标量只有大小,绝大多数情况下没有方向。我们通常用带箭头的线段来表示向量,在平面直角坐标系中的向量如下图所示。需要注意的是,向量是表达大小和方向的量,并没有规定起点和终点,所以相同的向量可以画在任意位置,例如下图中
向量有很多种代数表示法,对于二维空间的向量,下面几种写法都是可以的。
向量的大小称为向量的模,它是一个标量,对于二维空间的向量,模可以通过下面的公式计算。
注意,这里的
向量的加法
相同维度的向量可以相加得到一个新的向量,运算的方法是将向量的每个分量相加,如下所示。
向量的加法满足“平行四边形法则”,即两个向量
向量的数乘
一个向量
我们可以用 NumPy 的数组来表示向量,向量的加法可以通过两个数组的加法来实现,向量的数乘可以通过数组和标量的乘法来实现,此处不再进行赘述。
向量的点积
点积(dot product)是两个向量之间最为重要的运算之一,运算的方法是将两个向量对应分量的乘积求和,所以点积的结果是一个标量,其几何意义是两个向量的模乘以二者夹角的余弦如下所示。
假如我们用3维向量来表示用户对喜剧片、言情片和动作片这三类电影的偏好,我们用1到5的数字来表示喜欢的程度,其中5表示非常喜欢,4表示比较喜欢,3表示无感,2表示比较反感,1表示特别反感。那么,下面的向量表示用户非常喜欢喜剧片,特别反感言情片,对动作片不喜欢也不反感。
现在有两部电影上映了,一部属于言情喜剧片,一部属于喜剧动作片,我们把两部电影也通过3维向量的方式进行表示,如下所示。
如果现在我们需要向刚才的用户推荐一部电影,我们应该给他推荐哪一部呢?我们可以将代表用户的向量
大家可能会说,向量dot
函数来计算,而向量的模长可以通过 NumPy 的linalg
模块中的norm
函数来计算,代码如下所示。
python
u = np.array([5, 1, 3])
m1 = np.array([4, 5, 1])
m2 = np.array([5, 1, 5])
print(np.dot(u, m1) / (np.linalg.norm(u) * np.linalg.norm(m1))) # 0.7302967433402214
print(np.dot(u, m2) / (np.linalg.norm(u) * np.linalg.norm(m2))) # 0.9704311900788593
向量的叉积
在二维空间,两个向量的叉积是这样定义的:
对于三维空间,两个向量的叉积结果是一个向量,如下所示:
因为叉积的结果是向量,所以
NumPy 中可以通过cross
函数来计算向量的叉积,代码如下所示。
python
print(np.cross(u, m1)) # [-14 7 21]
print(np.cross(m1, u)) # [ 14 -7 -21]
行列式
行列式(determinant)通常记作
行列式的性质
行列式是由向量引出的,所以行列式解释的其实是向量的性质。
性质1:如果
性质2:如果
性质3:如果
性质4:如果
性质5:如果
性质6:将
性质7:将行列式的行列互换,行列式的值不变,如下所示。
性质8:方块矩阵
性质9:若
行列式的计算
对于二阶行列式,上面的公式相当于:
对于三阶行列式,上面的计算公式相当于:
高阶行列式可以用代数余子式(cofactor)展开成多个低阶行列式,如下所示:
其中,
矩阵
矩阵(matrix)是由一系列元素排成的矩形阵列,矩阵里的元素可以是数字、符号或数学公式。矩阵可以进行加法、减法、数乘、转置、矩阵乘法等运算,如下图所示。
值得一提的是矩阵乘法运算,该运算仅当第一个矩阵
例如:
矩阵的乘法满足结合律和对矩阵加法的分配律:
结合律:
左分配律:
右分配律:
矩阵乘法不满足交换律。一般情况下,矩阵
矩阵乘法的一个基本应用是在线性方程组上。线性方程组是方程组的一种,它符合以下的形式:
运用矩阵的方式,可以将线性方程组写成一个向量方程:
其中,
矩阵是线性变换(保持向量加法和标量乘法的函数)的便利表达法。矩阵乘法的本质在联系到线性变换的时候最能体现,因为矩阵乘法和线性变换的合成有以下的联系,即每个
下图是一个来自于维基百科的例子,图中展示了一些典型的二维实平面上的线性变换对平面向量(图形)造成的效果以及它们对应的二维矩阵,其中每个线性变换将蓝色图形映射成绿色图形;平面的原点
矩阵对象
NumPy 中提供了专门用于线性代数(linear algebra)的模块和表示矩阵的类型matrix
,当然我们通过二维数组也可以表示一个矩阵,官方并不推荐使用matrix
类而是建议使用二维数组,而且有可能在将来的版本中会移除matrix
类。无论如何,利用这些已经封装好的类和函数,我们可以轻松愉快的实现很多对矩阵的操作。
我们可以通过下面的代码来创建矩阵(matrix
)对象。
代码:
Python
m1 = np.matrix('1 2 3; 4 5 6')
m1
说明:
matrix
构造器可以传入类数组对象也可以传入字符串来构造矩阵对象。
输出:
matrix([[1, 2, 3],
[4, 5, 6]])
代码:
Python
m2 = np.asmatrix(np.array([[1, 1], [2, 2], [3, 3]]))
m2
说明:
asmatrix
函数也可以用mat
函数代替,这两个函数其实是同一个函数。
输出:
matrix([[1, 1],
[2, 2],
[3, 3]])
代码:
Python
m1 * m2
输出:
matrix([[14, 14],
[32, 32]])
说明:注意
matrix
对象和ndarray
对象乘法运算的差别,matrix
对象的*
运算是矩阵乘法运算。如果两个二维数组要做矩阵乘法运算,应该使用@
运算符或matmul
函数,而不是*
运算符。
矩阵对象的属性如下表所示。
属性 | 说明 |
---|---|
A | 获取矩阵对象对应的ndarray 对象 |
A1 | 获取矩阵对象对应的扁平化后的ndarray 对象 |
I | 可逆矩阵的逆矩阵 |
T | 矩阵的转置 |
H | 矩阵的共轭转置 |
shape | 矩阵的形状 |
size | 矩阵元素的个数 |
矩阵对象的方法跟之前讲过的ndarray
数组对象的方法基本差不多,此处不再进行赘述。
线性代数模块
NumPy 的linalg
模块中有一组标准的矩阵分解运算以及诸如求逆和行列式之类的函数,它们跟 MATLAB 和 R 等语言所使用的是相同的行业标准线性代数库,下面的表格列出了numpy
以及linalg
模块中一些常用的线性代数相关函数。
函数 | 说明 |
---|---|
diag | 以一维数组的形式返回方阵的对角线元素或将一维数组转换为方阵(非对角元素元素为0) |
matmul | 矩阵乘法运算 |
trace | 计算对角线元素的和 |
norm | 求矩阵或向量的范数 |
det | 计算行列式的值 |
matrix_rank | 计算矩阵的秩 |
eig | 计算矩阵的特征值(eigenvalue)和特征向量(eigenvector) |
inv | 计算非奇异矩阵( |
pinv | 计算矩阵的摩尔-彭若斯(Moore-Penrose)广义逆 |
qr | QR分解(把矩阵分解成一个正交矩阵与一个上三角矩阵的积) |
svd | 计算奇异值分解(singular value decomposition) |
solve | 解线性方程组 |
lstsq | 计算 |
下面我们简单尝试一下上面的函数,先试一试求逆矩阵。
代码:
Python
m3 = np.array([[1., 2.], [3., 4.]])
m4 = np.linalg.inv(m3)
m4
输出:
array([[-2. , 1. ],
[ 1.5, -0.5]])
代码:
Python
np.around(m3 @ m4)
说明:
around
函数对数组元素进行四舍五入操作,默认小数点后面的位数为0。
输出:
array([[1., 0.],
[0., 1.]])
说明:矩阵和它的逆矩阵做矩阵乘法会得到单位矩阵。
计算行列式的值。
代码:
Python
m5 = np.array([[1, 3, 5], [2, 4, 6], [4, 7, 9]])
np.linalg.det(m5)
输出:
2
计算矩阵的秩。
代码:
Python
np.linalg.matrix_rank(m5)
输出:
3
求解线性方程组。
对于上面的线性方程组,我们可以用矩阵的形式来表示它,如下所示。
线性方程组有唯一解的条件:系数矩阵
代码:
Python
A = np.array([[1, 2, 1], [3, 7, 2], [2, 2, 1]])
b = np.array([8, 23, 9]).reshape(-1, 1)
print(np.linalg.matrix_rank(A))
print(np.linalg.matrix_rank(np.hstack((A, b))))
说明:使用数组对象的
reshape
方法调形时,如果其中一个参数为-1,那么该维度有多少个元素是通过数组元素个数(size
属性)和其他维度的元素个数自动计算出来的。
输出:
3
3
代码:
Python
np.linalg.solve(A, b)
输出:
array([[1.],
[2.],
[3.]])
说明:上面的结果表示,线性方程组的解为:
。
下面是另一种求解线性方程组的方法,大家可以停下来思考下为什么。
代码:
Python
np.linalg.inv(A) @ b
输出:
array([[1.],
[2.],
[3.]])
多项式
除了数组,NumPy 中还封装了用于多项式(polynomial)运算的数据类型。多项式是变量的整数次幂与系数的乘积之和,形如:
在 NumPy 1.4版本之前,我们可以用poly1d
类型来表示多项式,目前它仍然可用,但是官方提供了新的模块numpy.polynomial
,它除了支持基本的幂级数多项式外,还可以支持切比雪夫多项式、拉盖尔多项式等。
创建多项式对象
创建poly1d
对象,例如:
代码:
python
p1 = np.poly1d([3, 2, 1])
p2 = np.poly1d([1, 2, 3])
print(p1)
print(p2)
输出:
2
3 x + 2 x + 1
2
1 x + 2 x + 3
多项式的操作
获取多项式的系数
代码:
python
print(p1.coefficients)
print(p2.coeffs)
输出:
[3 2 1]
[1 2 3]
两个多项式的四则运算
代码:
python
print(p1 + p2)
print(p1 * p2)
输出:
2
4 x + 4 x + 4
4 3 2
3 x + 8 x + 14 x + 8 x + 3
带入
代码:
python
print(p1(3))
print(p2(3))
输出:
34
18
多项式求导和不定积分
代码:
python
print(p1.deriv())
print(p1.integ())
输出:
6 x + 2
3 2
1 x + 1 x + 1 x
求多项式的根
例如有多项式
代码:
python
p3 = np.poly1d([1, 3, 2])
print(p3.roots)
输出:
[-2. -1.]
如果使用numpy.polynomial
模块的Polynomial
类来表示多项式对象,那么对应的操作如下所示。
代码:
python
from numpy.polynomial import Polynomial
p3 = Polynomial((2, 3, 1))
print(p3) # 输出多项式
print(p3(3)) # 令x=3,计算多项式的值
print(p3.roots()) # 计算多项式的根
print(p3.degree()) # 获得多项式的次数
print(p3.deriv()) # 求导
print(p3.integ()) # 求不定积分
输出:
2.0 + 3.0·x + 1.0·x²
20.0
[-2. -1.]
2
3.0 + 2.0·x
0.0 + 2.0·x + 1.5·x² + 0.33333333·x³
最小二乘解
Polynomial
类还有一个名为fit
的类方法,它可以给多项式求最小二乘解。所谓最小二乘解(least-squares solution),是用最小二乘法通过最小化误差的平方和来寻找数据的最佳匹配函数的系数。假设多项式为
例如,我们想利用收集到的月收入和网购支出的历史数据来建立一个预测模型,以达到通过某人的月收入预测他网购支出金额的目标,下面是我们收集到的收入和网购支出的数据,保存在两个数组中。
python
x = np.array([
25000, 15850, 15500, 20500, 22000, 20010, 26050, 12500, 18500, 27300,
15000, 8300, 23320, 5250, 5800, 9100, 4800, 16000, 28500, 32000,
31300, 10800, 6750, 6020, 13300, 30020, 3200, 17300, 8835, 3500
])
y = np.array([
2599, 1400, 1120, 2560, 1900, 1200, 2320, 800, 1650, 2200,
980, 580, 1885, 600, 400, 800, 420, 1380, 1980, 3999,
3800, 725, 520, 420, 1200, 4020, 350, 1500, 560, 500
])
我们可以先绘制散点图来了解两组数据是否具有正相关或负相关关系。正相关意味着数组x
中较大的值对应到数组y
中也是较大的值,而负相关则意味着数组x
中较大的值对应到数组y
中较小的值。
python
import matplotlib.pyplot as plt
plt.figure(dpi=120)
plt.scatter(x, y, color='blue')
plt.show()
输出:
如果需要定量的研究两组数据的相关性,我们可以计算协方差或相关系数,对应的 NumPy 函数分别是cov
和corrcoef
。
代码:
python
np.corrcoef(x, y)
输出:
array([[1. , 0.92275889],
[0.92275889, 1. ]])
说明:相关系数是一个
-1
到1
之间的值,越靠近1
说明正相关性越强,越靠近-1
说明负相关性越强,靠近0
则说明两组数据没有明显的相关性。上面月收入和网购支出之间的相关系数是0.92275889
,说明二者是强正相关关系。
通过上面的操作,我们确定了收入和网购支出之前存在强正相关关系,于是我们用这些数据来创建一个回归模型,找出一条能够很好的拟合这些数据点的直线。这里,我们就可以用到上面提到的fit
方法,具体的代码如下所示。
代码:
python
from numpy.polynomial import Polynomial
Polynomial.fit(x, y, deg=1).convert().coef
说明:
deg=1
说明回归模型最高次项就是1次项,回归模型形如;如果要生一个类似于 的模型,就需要设置 deg=2
,以此类推。
输出:
array([-2.94883437e+02, 1.10333716e-01])
根据上面输出的结果,我们的回归方程应该是
代码:
python
import matplotlib.pyplot as plt
plt.scatter(x, y, color='blue')
plt.scatter(x, 0.110333716 * x - 294.883437, color='red')
plt.plot(x, 0.110333716 * x - 294.883437, color='darkcyan')
plt.show()
输出:
如果不使用Polynomial
类型的fit
方法,我们也可以通过 NumPy 提供的polyfit
函数来完成同样的操作,有兴趣的读者可以自行研究。