一、numpy中矩阵的构造 以及 一些常用的方法

线性代数是计算数学最重要的组成部分之一。线性代数的研究对象是向量和矩阵。Numpy中包含有所有用来处理这些对象的必备工具。
首要任务是构建矩阵和向量,或者切片来更改它们。
另外一个任务,是dot运算,它包含了大多数线性代数运算(标量积、矩阵-向量乘机和矩阵-矩阵乘积)

1.1向量

#创建向量就是使用函数array将列表转换:

v=np.array([1,2,3])

print(v) #[1 2 3]

print(v.shape)#(3,)

#切片和索引
A=np.array([1,2,3,4,5,6])

A[0] #1

A[1:] #array([2, 3, 4, 5])

向量的各种基本运算;所有的基本算数运算都是在元素间进行的
2*v

v1/2

3v1+2v2

范数
from scipy.linalg import norm
norm(v)

标量积
dot(v1,v2)或v1@v2

1.2矩阵

#矩阵是用类似于创建向量的方式创建的,

#向量n,矩阵nx1和1xn即使包含相同的数据,也是三个不同的对象

M=np.array([[1,2],[0,1]]) #[1,2]/n[0,1]

1.2.1数组的一些属性

np.shape 或 np.shape(a) 计算矩阵形状
np.ndim 或 np.ndim(a) 计算维数
reshape() 函数在不复制数据的情况下给出了一个数组的新视图:
转置是一种特殊格式的重塑

M[2:4,]#仅在行2:4的切片
M[2:4,1:4]#是行和列的切片
M[2:4,1:4]#是行和列的切片

使用切片修改数组
M[1,3]=2 #标量
M[2,:]=[1,2,3]#向量

1.2.2切片和索引

可以通过索引来访问数组项。与向量系数不同,需要两个索引来访问矩阵系数,这些索引都在一对方括号中给出,这样就能将数组语法与一系列列表语法(需要两对方括号来访问元素)区分开来:

M=np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])

M[0,0]      #1
M[0]        #[1,2,3]
M[1:,]       #第二行之后的所有
M[:,1]       #第二列元素

M[0,0]
M[0,1]

数组切片与列表切片类似,但其可能存在多个维度。
M[i,] #取M的i行所填称的向量

M[:,j] #取M的j列所填充的向量# #一、numpy中矩阵的构造 以及 一些常用的方法

1.2.3构建特殊矩阵

我们一般通过列表来构造数组,但还有一些便捷的方法可用来生成特殊的数组

zeros(n,m) #形状(n,m) 由zeros填称的矩阵

ones(n,m) #由ones填称的矩阵

random.rand(n,m) #由在(0,1)中平均分布的随机数填填充的矩阵

arange(n) #前n个整数

linspace(a,b,n) #(n,) 由平均分布在a和b之间的n个点所组成的向量

identity() #生成指定大小的单位阵

1.2.4矩阵的拼接

用一对相匹配的子矩阵来构建矩阵的通用方法是**concatenate;**注意参数 axis

1.2.5作用于矩阵的函数

有许多不同类型的函数可用于数组。有些函数是基于元素的,它们返回一个与原数组具有相同形状的数组,被称为 通用函数.
其它数组函数则返回一个具有不同形状的数组。

(1)通用函数
通用函数是作用于数组元素的函数,因此他们具有与输入数组形状相同的输出数据。
例如:cos
创建通用函数:vectorize()

(2)矩阵函数
有许多作用于数组(而不是作用于元素)的函数,例如max、min和sum,mean,这些函数能以行或列的方式作用于整个矩阵。
当没有提供任何参数时,它们将作用于整个矩阵。

1.2.6线性代数运算

python中的dot函数是执行大多常见的线性代数运算的基本运算符,它可以用于矩阵-向量的乘法
dot(M,V)
M@V

1.2.7Scipy中的线性代数方法

Scipy在Scipy.linalg模块中提供了大量的数值线性代数方法,线性代数方法是科学计算中所有方法的核心,并且Scipy使用包装程序(wrappers)而不是
纯Python代码,使得这些核心方法运算非常快。

import scipy.linalg as sl
ls.det 矩阵的行列式

ls.eig 矩阵的特征值和特征向量

sl.inv 逆矩阵

sl.pinv 伪逆矩阵

sl.norm 矩阵或向量范数

sl.svd 奇异值分解

sl.solve 一般或对称的线性系统的解决方案

sl.lstsq 最小二乘解

2、高级数组

2.1数组视图和副本

2.1.1

为了精确控制内存的使用方式,Numpy包提供了数组视图的概念。视图是与较大数组共享相同数据的较小数组。
M=np.array([[1,2],[3,4]])

array([[1, 2],
[3, 4]])
v=M[0:,]
上述切片是数组M的视图,它与M共享相同的数据,如果修改v,M也会被修改。

关于哪些切片将返回视图,哪些切片将返回副本都有明确的规则。只有基本切片(主要为带冒号的索引表达式)将返回视图,而
任何高级切片(例如使用布尔值的切片)都将返回数据的副本。

#有一个特别简单的数组切片可以返回整个数组的视图:
N=M[:] #这是整个数组M的视图

2.1.2装置和重塑视图

(1)转置可以返回视图
(2)重塑可以返回视图

v=arrange(10)
C=v.reshape(-1,1)
C.base is v

2.1.3复制数组

有时需要显式地请求复制数据,这可以通过Numpy包的函数array很容易的实现:
v=np.array([[1,2],[3,4]])
n=np.array(v.T)

2.2布尔数组

2.2.1

布尔数组只是一个元素为Bool类型的数组:
A=np.array([True,False])
A.dtype #dtype(‘bool’)

任何作用于数组的比较运算符都将创建一个布尔数组,而不是简单的布尔值:
M=np.array([[1,2],[3,4]])
M>2
array([[False, False],
[ True, True]])

M==0
array([[False, False],
[False, False]])

注意,由于数组比较创建了布尔数组,因此在条件语句(例如if语句中) 不能直接使用数组比较,解决方案是 使用 all和any方法。

2.2.2相等判断

两个浮点数组的相等判断不是直接进行的,因为两个浮点数可能无限接近但不相等,在numpy中可以用 allclose来判断
相等,该函数判断了两个达到指定精度的数组是否相等。

2.2.3数组布尔运算

用户不能在布尔数组上使用and、or或not操作符,这些操作符实际上会将数组强制转换为布尔值,这是不被允许的。

用 & | ~代替

2.3数组索引

我们也已经了解到通过组合切片和整数可以索引数组,这是基本的切片技巧。然而我们还有更多的方法可用来访问和修改数组元素。

2.3.1使用布尔数组进行索引

根据数组中元素的值来访问和修改数组的一部分通常很有用。例如,我们可能想要访问数组中的所有正数,此时使用
布尔数组就是可行的。这种索引操作的结果总是一个向量,
B=np.array([[True,False],[False,True]])
M=np.array([[2,3],[1,4]])
M[B]
#array([2, 4])

实际上,调用M[B]相当于使用了方法M.flatten()[B]。然后,我们可以用另一个向量代替所得向量、例如可以用
0来替换所有元素:

M[B]=0
M
array([[0, 3],
[1, 0]])

或者可以用其它值来替换所有选定值:
M[B]=10,20
M
array([[10, 3],
[ 1, 20]])

为了组合布尔数组(M>2)的创建、智能索引(使用布尔数组进行索引)以及广播,可以使用如下优雅的语法:

M[M>2]=0 #将所有大于2的元素均被替换为0

2.3.2使用where命令
命令where给出了一个有用的结构,该结构将布尔数组作为条件,并返回满足条件的数组元素的索引:

其基本结构为: where(condition,a,b)
如果条件为true,则返回a值;如果条件为False,则返回b值

如果省略第二个和第三个参数,则返回一个元组,该元组包含了满足条件的元素索引。

3代码性能和向量化

3.1

最好尽可能的使用Numpy和Scipy包中的函数,而不是解释型代码版本。
诸如矩阵乘法、矩阵-向量乘法、矩阵分解以及标量乘积等Numpy数组运算要比任何纯Python代码的等效运算要快的多
考虑如下标量乘积的简单实例,标量乘积比编译的 Numpy函数 dot(a,b)要慢的多。(对于大约有100个元素的数组,其速度要慢100倍)
import numpy as np
def my_prod(a,b):
val=0
for aa,bb in zip(a,b):
val+=aa*bb
return val

a_list=range(1,100)
b_list=range(1,100)
print(my_prod(a_list,b_list))
print(np.dot(a_list,b_list))

3.2代码性能和向量化

最好尽可能的使用Numpy和Scipy包中的函数,而不是解释型代码版本。
诸如矩阵乘法、矩阵-向量乘法、矩阵分解以及标量乘积等Numpy数组运算要比任何纯Python代码的等效运算要快的多
考虑如下标量乘积的简单实例,标量乘积比编译的 Numpy函数 dot(a,b)要慢的多。(对于大约有100个元素的数组,其速度要慢100倍)
import numpy as np
def my_prod(a,b):
val=0
for aa,bb in zip(a,b):
val+=aa*bb
return val

a_list=range(1,100)
b_list=range(1,100)
print(my_prod(a_list,b_list))
print(np.dot(a_list,b_list))

向量化

为了提升代码性能,我们通常必须将代码向量化。使用Numpy包的切片、运算符和函数来替换代码中的for 循环以及其他 运行速度较慢的 代码片段,可以显著地提高代码的性能。

例如:通过元素迭代来实现 标量和向量 的简单加法运算,运行速度是非常慢的:

v=np.array([1,2,3,4,5])
for i in range(len(v)):
v[i]=v[i]+5

使用Numpy包的加法,运行速度要比上面的代码要快的多
v=v+5

3.3vectorize()函数


这里需要一个函数,并创建 一个向量化的版本。该版本 通过函数尽可能地将 该函数应用于数组的所有元素。

def my_func(x):
y=x**3-2*x+5
if y>0.5:
return y-0.5
else:
return 0

(1) 通过遍历数组来使用该韩顺你会使得运行速度非常慢

for i in range(len(v)):
v[i]=my_func(v[i])

(2)相反,可以使用函数vectorize来创建一个新函数,如下所示
my_vecfun=np.vectorize(my_func)
v=my_vecfun(v)

4广播

Numpy中的广播表示能够猜测两个数组之间共同以及兼容的形状。例如,当一个向量(一维数组)和一个标量(0维数组)相加
时,为了能够执行加法,标量需要扩展为向量,这种通用机制称为广播。

b=np.arange(2).reshape(1,-1) #row
a=np.arange(2).reshape(-1,1) #column
a+b
#valid addtion
array([[0, 1],
[1, 2]])

4.1广播数组

(1)当numpy被给定两个不同形状的数组,并要求执行满足两个形状相同的数组运算时,两个数组均被广播成常见的形状。
(2) 要将长度为n的向量v自动广播到形状为(n,m)是不可行的
(3)reshape