Python库第一课:基础Numpy知识(下):矩阵

        好的,我们今天继续来学习Numpy的基础,昨天,已经介绍完Numpy的成员之一——数组,今天,在接着介绍其另一大成员——矩阵,也是应用非常广泛的成员。

        矩阵,在线性代数中是几乎贯穿全文的成员,因此,这里需要较高的线性代数的基础。在这里,默认对线性代数有全面的学习与认识,了解最基本的矩阵性质及运算。我们这里对于线性代数部分主要用于介绍,对变成部分新颖与重要部分进行展示,便于以后的记忆。

        因此,本章学习还是概念多余代码,对于代码,是比较简单的。

一、矩阵的基本属性与计算

 1、数组的创建和转置

        对数组,创建也有特定的函数,其函数为:matrix(),其可以将列表、元组、range对象等转换为矩阵,其基础的转换举例如下:

import numpy as np


# 创建基础的矩阵
matr_base = np.matrix([[1, 2, 3], [4, 5, 6]])
matr_base_2 = np.matrix([1, 2, 3, 4, 5, 6])
# 逐层访问与单词访问的区别
print(matr_base[1, 0])
print(matr_base[1][0])

        其输出的结果为:4和[[4 5 6]]。由此,我们可以推测,对于矩阵一般只会存在单次访问,如果是数组,此时两者输出的结果应当是相等的。对数组来说,其深入访问的方式为:先横后纵的访问方式。

        然而,对于矩阵来说,是不存在层层深入访问的,其所有的访问都是同类型的,都是访问的横排,因此,深层访问基本不存在。

        紧接着,是矩阵的转置,对于矩阵的转置,如同矩阵的转置的写法一般,全文后面matr为矩阵举例。因此,要将该矩阵转置,只需要输入matr.T即可得到转置矩阵。矩阵的转置无非是将每一横行写成纵行,第二横行改为第二纵行,由此,将局长你转置完成。

 2、矩阵的特征

        在这里,列举矩阵中numpy内置的部分函数,用于求矩阵的基本数。

# 所有元素的平均值
print(matr_base.mean())
# 纵向每列平均值
print(matr_base.mean(axis=0))
# 数组的形状
print(matr_base.mean(axis=0).shape)
# 横向每行平均值
print(matr_base.mean(axis=1))
# 所有元素之和
print(matr_base.sum())
# 横向最大值
print(matr_base.max(axis=1))
# 横向最大值的下标
print(matr_base.argmax(axis=1))
# 对角线的元素
print(matr_base.diagonal())
# 非0元素的下标,分别返回行下标和列下标
print(matr_base.nonzero())

        输出的结果如下:

4
[[4 5 6]]
3.5
[[2.5 3.5 4.5]]
(1, 3)
[[2.]
 [5.]]
21
[[3]
 [6]]
[[2]
 [2]]
[[1 5]]
(array([0, 0, 0, 1, 1, 1], dtype=int64), array([0, 1, 2, 0, 1, 2], dtype=int64))

 3、矩阵乘法

        对矩阵乘法,一直有一个公式,那就是“左列=右行”方可进行计算,计算出来的结果为“左行右列”。这是我现代自己便于记忆的公式。翻译过来就是:左边的列数必须与右边的行数相等,乘出来的矩阵行数为左边矩阵的行数,列数为右边矩阵的列数。

       非常值得注意的一点为: 矩阵的乘法不满足交换律,交换之后可能无法相乘。

# 矩阵的乘法
matr_one=np.matrix([1,2,3]) ; matr_two=np.matrix(range(1,10))
matr_two.shape=3,3 ; matr_mult=matr_one*matr_two
print(matr_mult)

        其输出的结果为:[[30 36 42]]。

        可以看到,1行3列矩阵与3行3列矩阵相乘,满足上述两个条件,得到的结果也如上。如果交换顺序,便会产生错误:

Traceback (most recent call last):
  File "d:\PythonLearn\PythonWork\专题Numpy基础(下)_矩阵.py", line 36, in <module>      
    print(matr_two*matr_one)
          ~~~~~~~~^~~~~~~~~
  File "D:\Python312\Lib\site-packages\numpy\matrixlib\defmatrix.py", line 219, in __mul__
    return N.dot(self, asmatrix(other))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: shapes (3,3) and (1,3) not aligned: 3 (dim 1) != 1 (dim 0)

        我们可以看到啊,它指出了3!=1,两者不等,不能进行相乘。

 4、相关系数矩阵与三差

        对于相关系数矩阵,就是一个对称阵(关于主对角线完全对称),主对角线上的元素全都是1,副对角线上的元素表示对应位置的相关系数,每个元素的绝对值小于1。权威正则正相关,相反则负相关。numpy中用corrcoef()函数计算相关矩阵。

# 计算相关矩阵
# 负相关矩阵
print(np.corrcoef([[1, 2, 3, 4], [4, 3, 2, 1]]))
print(np.corrcoef([[1, 2, 3, 4], [8, 3, 2, 1]]))
# 正相关矩阵
print(np.corrcoef([[1, 2, 3, 4], [1, 2, 3, 4]]))
print(np.corrcoef([[1, 2, 3, 4], [1, 2, 3, 40]]))

        最终得到的结果如下:

[[ 1. -1.] 
 [-1.  1.]]
[[ 1.         -0.91350028] 
 [-0.91350028  1.        ]]
[[1. 1.]
 [1. 1.]]
[[1.        0.8010362]
 [0.8010362 1.       ]]

        下面,我们在学习对三差的计算:方差、标准差、协方差。其中,标准差与协方差在概率论里面涉及较为多,前两个的定义已经耳熟能详了,对于协方差,概率论的定义为:

期望值分别为E[X]与E[Y]的两个实随机变量X与Y之间的协方差Cov(X,Y)定义为:

        实际协方差也为方差的特殊种类,这里只管应用,在数学建模领域,这个是个重要的概念,大家是需要深入学习的。我们这里只学函数应用。std()函数表示保准差,cov表示方差,合理应用就是协方差。以下有例子:

# 三差的计算
x = [-2.1, -1, 4.3] ; y = [3, 1.1, 0.12] ; matr_dif = np.vstack((x, y))
# 协方差
print(np.cov(matr_dif))
print(np.cov(x,y))
# 标准差
print(np.std(matr_dif))
print(np.std(matr_dif, axis=1))
# 方差
print(np.cov(x))

        起计算原理是一个简单的数学问题,直接看其运行的结果为:

[[11.71       -4.286     ] 
 [-4.286       2.14413333]]
[[11.71       -4.286     ] 
 [-4.286       2.14413333]]
2.2071223094538484
[2.79404128 1.19558447]    
11.709999999999999

       

 5、计算特征值与特征向量

        首先我们需要知道特征值是针对方阵所特殊的存在,其线性代数的计算方法为A·\xi =\lambda \xi,变形后得到\left | \lambda \xi -A \right |=0,因此,需要用这个公式对原A方阵进行变形,最后才能得到其一个或者多个特征根。

        在numpy中,其子模块linalg中,提供的特征值与特征向量为eig()函数。其函数的使用格式为:

# 特征值与特征向量
matr_esp = np.matrix([[2, -2, 0], [-2, 1, -2], [0, -2, 0]])
nmd_matr, st_matr = np.linalg.eig(matr_esp)
print(nmd_matr, st_matr, sep='\n')
print(type(nmd_matr), type(st_matr), sep='\n')

        特征值,特征向量 = np.linalg.eig(matr),因此,基于此规则,对特征值的求法就是一个函数,但这里需要注意的是,求矩阵的特征向量,建立时应当采用数组起步。但实际上,只有矩阵计算特征香菜才具有意义的,特征向量是矩阵的一重大点。以上结果的输出为:

[ 4.  1. -2.]
[[-0.66666667 -0.66666667  0.33333333] 
 [ 0.66666667 -0.33333333  0.66666667] 
 [-0.33333333  0.66666667  0.66666667]]
<class 'numpy.ndarray'>
<class 'numpy.matrix'>

        可以看到,特征值输出的结果为数组,特征向量输出的结果为矩阵。在这里需要注意的是,三个特征向量是按照列查看的,也就是三列特征值,水平堆叠hstack形成一个特征矩阵。

        在线性代数中,我们运用史密斯正交法,将相关向量化为无关向量,并单位正交化,可以得到一个全新矩阵,此矩阵乃是特征矩阵。接下来可以进行验证:

# 特征之一特征向量的相互计算
# 计算矩阵与特征向量乘积
print(np.dot(matr_esp, st_matr))
# 计算特征值与特征向量的乘积
print(nmd_matr*st_matr)
# 验证二者是否相等
print(np.isclose(np.dot(matr_esp, st_matr), nmd_matr*st_matr))
# 计算其行列式的值
print(np.linalg.det(matr_esp-np.eye(3, 3)*nmd_matr))

        输出结果如下:

[[-2.66666667 -0.66666667 -0.66666667] 
 [ 2.66666667 -0.33333333 -1.33333333] 
 [-1.33333333  0.66666667 -1.33333333]]
[[-2.66666667 -0.66666667 -0.66666667] 
 [ 2.66666667 -0.33333333 -1.33333333] 
 [-1.33333333  0.66666667 -1.33333333]]
[[ True  True  True]
 [ True  True  True]
 [ True  True  True]]
-6.217248937900884e-15

        其结果成立,便为特征值。满足原表达式。

 6、计算逆矩阵        

        接下来是最后一个计算知识点,也是线性代数中重点,且每年的必考点之一:计算逆矩阵。我们知道,常规的逆矩阵及算法有三种:(1)用定义求逆矩阵;(2)用伴随矩阵求逆;(3)初等变换——行变换增广矩阵化单位阵求逆矩阵。

        而在python中,计算逆矩阵的方法便为初等变换,这里简单书写提醒原则,也是我自己在回顾一遍:

        当两个方阵A和B进行乘法(位置不可交换),A·B=B·A=E,则A^{-1}=B,因此,基于此逻辑,我么可以将一个单位阵与B进行增广,达成B|E,通过初等恒变换,将B矩阵化为单位阵E通过恒等变换得到的结果自然为A的逆矩阵,其等价于B|E=E|A^{-1}

       其原理在于,我们在将B化为单位阵的过程中,实际上经历了以下变化:B——>BA——>E,此过程中,E的变化为:E——>EA^{-1}——>A^{-1}。因此,上述结果成立。这是求B的逆矩阵A时满足的结果,以结果逆推其逆矩阵。

        在python中,将此过程表达出来的仍然是linalg模块中的函数inv()。其表达式为:逆=np.linalg.inv(matr)。此计算,就必定采用矩阵作为基础,这一点与计算矩阵的特征值与特征向量是具有一定区别的。

# 计算矩阵的逆矩阵
init_matr = np.matrix([[1, 2, 3], [4, 5, 6], [7, 8, 0]])
contr_matr = np.linalg.inv(init_matr)
print(contr_matr)
# 若验证结果可以使用是否相等
print(init_matr*contr_matr) ; print(contr_matr*init_matr)
print(np.isclose(init_matr*contr_matr, contr_matr*init_matr))

        其输出的结果为:

[[-1.77777778  0.88888889 -0.11111111] 
 [ 1.55555556 -0.77777778  0.22222222] 
 [-0.11111111  0.22222222 -0.11111111]]
[[ 1.00000000e+00  5.55111512e-17  1.38777878e-17] 
 [ 5.55111512e-17  1.00000000e+00  2.77555756e-17] 
 [ 1.77635684e-15 -8.88178420e-16  1.00000000e+00]]
[[ 1.00000000e+00 -1.11022302e-16  0.00000000e+00] 
 [ 8.32667268e-17  1.00000000e+00  2.22044605e-16] 
 [ 6.93889390e-17  0.00000000e+00  1.00000000e+00]]
[[ True  True  True]
 [ True  True  True]
 [ True  True  True]]

        至此,其逆矩阵我们便轻松拿捏了。

二、矩阵的应用拓展

 1、求解线性方程组

        在线性代数中,求解线性方程组是一个难点,其对于线性方程组要有一定的判定方可着实对方程组进行解答,这也是线性方程组的一个特殊点与重点所在。

        我们求解线性方程组的解步骤为:

        (1)化阶梯型;(2)找到最大线性无关组组成矩阵;(3)求自由变量s=n(列向量个数)-r(阶梯矩阵的秩);(4)取自由变量的单位矩阵;(5)计算出原始的列向量的每一个值,由此得到齐次线性方程的通解,至此,齐次线性方程组结束;(6)此步为非齐次特解的求法,通过自定自由元素的值,得到一个特解,非齐次线性方程组的解便为齐次的解+非齐次的特解。

        又是一个巨大的运算量,然而此过程在python中numpy已经有函数加以计算,仍然是linalg模块,solve()函数得到轻松解决。

# 唯一解
A = np.matrix([[3,1],[1,2]])
b = np.matrix([9,8]).T
x = np.linalg.solve(A,b)
print(x)
print(np.isclose(A*x,b))
# 最小二乘解:返回解、余项、a的秩、a的奇异值
print(np.linalg.lstsq(A,b,rcond= 1))

        我们已经知道线性方程组的解法为初始矩阵A与b的列项俩个的增广矩阵之间的关系。因此,solve函数就在这期间将a单独拿出来作为一个矩阵运用solve函数便可直接得到线性方程组的解。但此方案的局限性在于只适用于有解且只有唯一解恶的线性方程组。其解答为:

[[2.] 
 [3.]]
[[ True]
 [ True]]
(matrix([[2.],
        [3.]]), matrix([], shape=(1, 0), dtype=float64), 2, array([3.61803399, 1.38196601]))

         而对于多解的线性方程组,无论是齐次,还是非齐次,都涉及到很多交叉的变化,这里不再基础中提到,将在后面的numpy进阶中作为示例列举与学习。

 2、矩阵的向量化

        矩阵无法直接使用math库里面的阶乘函数factorial(),而numpy库也无内置函数解决,因此,可以用numpy中的vectorize()将矩阵向量化,便可使用阶乘函数。具体代码如下:

# 矩阵向量化
import math
matr =np.matrix([[1,2,3],[4,5,6]])
# 这里不能直接使用会报错
# math.factorial(matr)
# 正确方法先采用向量化
VecFactorial = np.vectorize(math.factorial())
print(VecFactorial(matr))

        最后可得到其阶乘的结果:

[[  1   2   6]
 [ 24 120 720]]

 3、奇异值分解

        接着,是今天的最后一个内容,奇异值分解,我们知道,存在可逆矩阵P、Q有P^{T}AQ=D(对角矩阵),则A=PDQ^{T}由此,通过奇异值分解,将矩阵A分解为多个更小的矩阵的乘积,这也是降维的一大方法。

        这一过程可以通过nmpy中linalg中的svd()函数完成,可以将a=usv,直接可用svd函数表示,其中s便为奇异值。

# 返回矩阵的奇异值
a =np.matrix([[1,2,3],[4,5,6],[7,8,9]])
u,s,v = np.linalg.svd(a)
print(s)

        其结果为:s=[1.68481034e+01 1.06836951e+00 4.41842475e-16]。

        至此,我们的矩阵到此结束,numpy基础知识也到此趋近于结束。每天学习,点滴进步!

  • 21
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值