目录
十一、线性代数
线性代数(如:矩阵乘法、矩阵分解、行列式以及其他方阵数学等)是任何数组库的重要组成部分。不像某些语言(如:MATLAB),通过*对两个二维数组相乘得到的是一个元素级的积,而不是矩阵点积。因此NumPy提供了一个用于矩阵乘法的dot()函数。
例子1:
m行k列的数组 点乘 k行n列的数组 得到一个m行n列的数组。(k必须对应相等)
x = np.array([[1, 2, 3], [4, 5, 6]])
y = np.array([[6, 23], [-1, 7], [8, 9]])
print(x.dot(y))
# np.dot(x, y)
[[ 28 64]
[ 67 181]]
例子2:
多维数组乘以一维数组,结果得到一维数组
np.dot(x, np.ones(3))
# [6., 5.]
NumPy中有一组标准的矩阵分解运算以及诸如求:逆、行列式等。他们跟MATLAB和R等语言所使用的是相同的行业标准级Fortran库,如:BLAS、LAPACK、Inter MKL等等,它也取决于你的Numpy版本。
from numpy.linalg import inv, qr
import numpy as np
X = np.random.randn(5, 5)
mat = X.T.dot(X)
print(inv(mat))
# 逆矩阵
[[ 2.38676625 0.0383446 1.22607032 -1.22301455 1.0457165 ]
[ 0.0383446 0.89091613 -0.85883745 -0.98421099 1.40459633]
[ 1.22607032 -0.85883745 2.17537261 0.88836567 -1.83227867]
[-1.22301455 -0.98421099 0.88836567 2.70466106 -3.56901905]
[ 1.0457165 1.40459633 -1.83227867 -3.56901905 5.19884975]]
result = mat.dot(inv(mat))
print(result)
# 结果为单位矩阵(矩阵 点乘 它的逆矩阵)
[[ 1.00000000e+00 -1.35244046e-16 -4.33027096e-16 -9.40317599e-16
6.35775695e-17]
[-9.61938278e-17 1.00000000e+00 1.45119917e-16 4.27620132e-16
1.34304975e-15]
[-1.96936202e-15 -1.45718601e-15 1.00000000e+00 3.04388831e-15
-1.89553893e-15]
[-7.19491779e-17 -3.02262390e-16 8.72327184e-16 1.00000000e+00
-6.47110058e-16]
[ 1.21385063e-16 -1.67329489e-16 -4.21602789e-16 -2.27367980e-16
1.00000000e+00]]
q, r = qr(mat)
print(r)
# 上三角矩阵
[[-3.61408516 0.84443702 3.54093823 -3.47532695 -0.63147377]
[ 0. -3.6491614 -2.81565423 -7.12586627 -4.94495531]
[ 0. 0. -1.76235213 -5.81463287 -4.72452019]
[ 0. 0. 0. -1.50171095 -1.05653428]
[ 0. 0. 0. 0. 0.14713904]]
print(q)
# 标准正交列矩阵
[[-0.85043878 -0.01382966 -0.41096514 0.16368423 0.28439014]
[-0.05424249 -0.77067525 0.12413786 -0.54821699 0.29523778]
[ 0.4553698 -0.30573961 -0.80115779 0.23297692 0.05504161]
[ 0.09014986 0.53724225 -0.35238216 -0.73203806 0.20782411]
[ 0.24150151 0.15415076 0.22286869 0.28724144 0.88641777]]
常用的np.linalg()函数
函数 | 说明 |
---|---|
diag | 以一维数组的形式返回方阵的对角线(或非对角线)元素,或将一维数组转换为方阵(非对角线元素为0) |
dot | 矩阵乘法 |
trace | 计算对角线元素和 |
det | 计算矩阵行列式 |
eig | 计算方阵的本征值和本征向量 |
inv | 计算方阵的逆 |
pinv | 计算方阵的Moore-Penrose伪逆 |
qr | 计算QR分解 |
svd | 计算奇异值分解(SVD) |
solve | 解线性方程组Ax=b,其中A为一个方阵 |
lstsq | 计算Ax=b的最小二乘解 |
十二、数组文件
Numpy能够读写磁盘上的文本数据或二进制数据。
1. 二进制格式保存
默认情况下,数组是以未压缩的原始二进制格式保存在扩展名为.npy的文件中。
NumPy 为ndarray对象引入了一个简单的文件格式。 这个npy文件在磁盘文件中,存储重建ndarray所需的数据、图形、dtype和其他信息,以便正确获取数组,即使该文件在具有不同架构的另一台机器上。
读写磁盘数组数据的三个主要函数:
# 单文件存储,文件结尾:.npy
np.save(file, array, allow_pickle=True, fix_imports=True)
# 多文件压缩存储,文件结尾:.npz
np.savez(file, *args, **kwds)
# 读取
np.load(file, mmap_mode=None, allow_pickle=False, fix_imports=True,
encoding='ASCII')
若文件路径末尾没有主动加上扩展名.npy,则该扩展名会被自动加上。然后就可以通过np.load()读取磁盘上的数组。
例子:
(1). 单文件
存储:
arr1 = np.array([3, 9, 7, 3, 1, 5, 8, 1])
np.save("my_array", arr1)
读取:
print(np.load('my_array.npy'))
# [3 9 7 3 1 5 8 1]
(2). 多文件
存储:
arr1 = np.array([3, 9, 7, 3, 1, 5, 8, 1])
arr2 = np.array([1, 2, 3, 4, 5, 6])
np.savez("my_zip", a=arr1, b=arr2)
读取:
result = np.load('my_zip.npz')
print(result['b'])
# [1 2 3 4 5 6]
save()和load()函数接受一个附加的布尔参数allow_pickles。 Python 中的pickle用于在保存到磁盘文件或从磁盘文件读取之前,对对象进行序列化和反序列化。
2. 文本格式保存
以简单文本文件格式存储和获取数组数据,是通过savetxt()和loadtx()函数完成的。
# 存储
np.savetxt(fname, X, fmt='%.18e', delimiter=' ', newline='\n', header='',
footer='', comments='# ', encoding=None)
# np.genfromtxt与np.savetxt类似,只不过是面向结构化数组和缺失数据处理
参数 | 说明 |
---|---|
fname | 取的文件名 |
X | 数组对象名 |
fmt | 字符串或者字符串序列,如:(%10.5f),其他复杂格式可参考底层代码 |
delimiter | 列分隔符,一般为’,'逗号分隔 |
newline | 行分隔符 |
header | 头注,写在文件开头 |
footer | 角注,写在文件结尾 |
comments | 标注符号,写在header和footer前面,默认为’#’,也可写成’numpy.loadtxt’等 |
encoding | 编码输出文件 |
np.loadtxt(fname, dtype=float, comments='#', delimiter=None,
converters=None, skiprows=0, usecols=None, unpack=False,
ndmin=0, encoding='bytes', max_rows=None)
参数 | 说明 |
---|---|
dtype | 输出文件数据类型,默认为float |
converters | 一个字典,映射列序号给一个函数,该函数可以解析字符串为目标值 |
skiprows | 整型。跳过的行,默认为0行 |
usecols | 整型或者元组序列。主动选择要读取的列。 |
unpack | 返回被转化过的数组。默认False |
ndim | 整型,返回至少该维度的数组 |
maxrows | 返回’skiprows’行后,读取’maxrows’行后的内容。默认读取所有行。 |
例子:
arr1 = np.array([3, 9, 7, 3, 1, 5, 8, 1])
np.savetxt("my_txt", arr1)
np.loadtxt('my_txt')
# [3. 9. 7. 3. 1. 5. 8. 1.]
3. 数据的CSV文件存取
(1)CSV文件
CSV (Comma-Separated Value,逗号分隔值)
CSV是一种常见的文件格式,用来存储批量数据。
(2)如何将数组写入CSV文件?
np.savetxt(frame, array, fmt='%.18e', delimiter=None) #介绍部分参数
参数 | 说明 |
---|---|
frame | 文件、字符串或产生器,可以是.gz或.bz2的压缩文件。 |
array | 存入文件的数组。 |
fmt | 写入文件的格式,例如:%d、%.2f 、%.18e。 |
delimiter | 分割字符串,默认是任何空格。 |
例子:
a = np.arange(18).reshape(2,9)
"""
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8],
[ 9, 10, 11, 12, 13, 14, 15, 16, 17]])
"""
np.savetxt('a.csv', a, fmt='%d', delimiter=',') #将数组保存成CSV文件
fo = open('a.csv',"r") #打开a.csv文件
for line in fo:
print(line) #在屏幕上显示文件内容
"""
0,1,2,3,4,5,6,7,8
9,10,11,12,13,14,15,16,17
"""
fo.close() #关闭文件
(3)如何读取CSV文件到数组?
np.loadtxt(frame, dtype=np.float, delimiter=None, unpack=FaIse) #介绍部分参数
参数 | 说明 |
---|---|
frame | 文件、字符串或产生器,可以是.gz或.bz2的压缩文件。 |
dtype | 数据类型,可选。 |
delimiter | 分割字符串,默认是任何空格。 |
unpack | 如果True,读入属性将分别写入不同变量。 |
例子:
b = np.loadtxt('a.csv', delimiter=',')
print(b)
"""
array([[ 0., 1., 2., 3., 4., 5., 6., 7., 8.],
[ 9., 10., 11., 12., 13., 14., 15., 16., 17.]])
"""
b = np.loadtxt('a.csv', dtype=np.int, delimiter=',') #改变数据类型
print(b)
"""
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8],
[ 9, 10, 11, 12, 13, 14, 15, 16, 17]])
"""
c = np.loadtxt('a.csv', dtype=np.int, delimiter=',', unpack=True)
print(c)
"""
array([[ 0, 9],
[ 1, 10],
[ 2, 11],
[ 3, 12],
[ 4, 13],
[ 5, 14],
[ 6, 15],
[ 7, 16],
[ 8, 17]])
"""
q,w,e,r,t,y,u,i,o = np.loadtxt('a.csv', dtype=np.int, delimiter=',', unpack=True) #读入到不同数组
print(q)
# array([0, 9])
print(o)
# array([ 8, 17])
(4)CSV文件的局限性
CSV只能有效存储一维和二维数组,也就是说np.savetxt()和np.loadtxt()只能有效存取一维和二维数组。
4. 多维数据的存取
(1) 任意维度数据存取
a.tofile(frame, sep=", format='%s')
参数 | 说明 |
---|---|
frame | 文件、字符串或产生器,可以是.gz或.bz2的压缩文件。 |
sep | 数据分割字符串,如果是空串,写入文件为二进制。 |
format | 写入数据的格式。 |
例子:
a = np.arange(24).reshape(2,3,4)
print(a)
"""
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
"""
a.tofile("b.dat", sep=',', format='%d')
f = open("b.dat", 'r')
for line in f:
print(line) #显示文件内容
# 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23
f.close()
(2)读取文件还原数据
np.fromfile(frame, dtype=float, count=-1, sep=")
参数 | 说明 |
---|---|
frame | 文件、字符串或产生器,可以是.gz或.bz2的压缩文件。 |
dtype | 数据类型,可选。 |
count | 读入元素个数,-1表示读入整个文件。 |
sep | 数据分割字符串,如果是空船,写入文件为二进制。 |
读取文本文件
a = np.arange(24).reshape(2,3,4)
a.tofile("b.dat", sep=',', format='%d') #生成文本文件,维度信息丢失
c = np.fromfile("b.dat", dtype=np.int, sep=',')
print(c)
"""
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23])
"""
c = np.fromfile("b.dat", dtype=np.int, sep=',').reshape(2,3,4) #变换数组维度
print(c)
"""
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
"""
读取二进制文件
a = np.arange(24).reshape(2,3,4)
a.tofile("b.dat", format='%d') #生成二进制文件,维度信息丢失
c = np.fromfile("b.dat", dtype=np.int)
print(c)
"""
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23])
"""
c = np.fromfile("b.dat", dtype=np.int).reshape(2,3,4) #变换数组维度
print(c)
"""
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
"""
注意:
①该方法读取时需要知道存入文件时数组的维度和元素类型;②a.tofile()和np.fromfile()可以配合使用达到数据缓存的目的;③可以通过元数据文件来存储额外信息。
十三、副本和视图
在执行函数时,其中一些返回输入数组的副本,而另一些返回视图。 当内容物理存储在另一个位置时,称为副本。 另一方面,如果提供了相同内存内容的不同视图,我们将其称为视图。
1. 无复制
简单的赋值不会创建数组对象的副本。 相反,它使用原始数组的相同id()来访问它。 id()返回 Python 对象的通用标识符,类似于 C 中的指针。
此外,一个数组的任何变化都反映在另一个数组上。 例如,一个数组的形状改变也会改变另一个数组的形状。
例子:
import numpy as np
a = np.arange(6)
print('我们的数组是:')
print(a)
# [0 1 2 3 4 5]
print('调用 id() 函数:')
print(id(a))
# 139747815479536
print('a 赋值给 b:')
b = a
print(b)
print('b 拥有相同 id():')
print(id(b))
# 139747815479536
print('修改 b 的形状:')
b.shape = 3,2
print(b)
"""
[[0 1]
[2 3]
[4 5]]
"""
print('a 的形状也修改了:')
print(a)
"""
[[0 1]
[2 3]
[4 5]]
"""
2. 视图或浅复制
NumPy 拥有ndarray.view()方法,它是一个新的数组对象,并可查看原始数组的相同数据。 与前一种情况不同,新数组的维数更改不会更改原始数据的维数。
例子:
import numpy as np
# 最开始 a 是个 3X2 的数组
a = np.arange(6).reshape(3,2)
print '数组 a:'
print a
print '创建 a 的视图:'
b = a.view()
print b
print '两个数组的 id() 不同:'
print 'a 的 id():'
print id(a)
print 'b 的 id():'
print id(b)
# 修改 b 的形状,并不会修改 a
b.shape = 2,3
print 'b 的形状:'
print b
print 'a 的形状:'
print a
3. 深复制
ndarray.copy()函数创建一个深层副本。 它是数组及其数据的完整副本,不与原始数组共享。
例子:
import numpy as np
a = np.array([[10,10], [2,3], [4,5]])
print('数组 a:')
print(a)
"""
[[10 10]
[ 2 3]
[ 4 5]]
"""
print('创建 a 的深层副本:'
b = a.copy()
print('数组 b:')
print(b)
"""
[[10 10]
[ 2 3]
[ 4 5]]
"""
# b 与 a 不共享任何内容
print('我们能够写入 b)来写入 a 吗?')
print(b is a)
# False
print('修改 b)的内容:'
b[0,0] = 100
print('修改后的数组 b:')
print(b)
"""
[[100 10]
[ 2 3]
[ 4 5]]
"""
print('a 保持不变:')
print(a)
"""
[[10 10]
[ 2 3]
[ 4 5]]
"""
十四、位操作
下面是 NumPy 包中可用的位操作函数。
函数 | 说明 |
---|---|
bitwise_and | 对数组元素执行位与操作 |
bitwise_or | 对数组元素执行位或操作 |
invert | 计算位非 |
left_shift | 向左移动二进制表示的位 |
right_shift | 向右移动二进制表示的位 |
1. bitwise_and
通过np.bitwise_and()函数对输入数组中的整数的二进制表示的相应位执行位与运算。
例子
import numpy as np
print('13 和 17 的二进制形式:')
a,b = 13,17
print(bin(a), bin(b))
# 0b1101 0b10001
print('13 和 17 的位与:')
print(np.bitwise_and(13, 17))
# 1
你可以使用下表验证此输出。 考虑下面的位与真值表。
A | B | AND |
---|---|---|
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
1 | 1 | 0 | 1 | ||
---|---|---|---|---|---|
AND | |||||
1 | 0 | 0 | 0 | 1 | |
result | 0 | 0 | 0 | 0 | 1 |
2. bitwise_or
通过np.bitwise_or()函数对输入数组中的整数的二进制表示的相应位执行位或运算。
例子
import numpy as np
a,b = 13,17
print('13 和 17 的二进制形式:')
print(bin(a), bin(b))
# 0b1101 0b10001
print('13 和 17 的位或:')
print(np.bitwise_or(13, 17))
#
29
你可以使用下表验证此输出。 考虑下面的位或真值表。
A | B | OR |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
1 | 1 | 0 | 1 | ||
---|---|---|---|---|---|
OR | |||||
1 | 0 | 0 | 0 | 1 | |
result | 1 | 1 | 1 | 0 | 1 |
3. invert
此函数计算输入数组中整数的位非结果。 对于有符号整数,返回补码。
例子
import numpy as np
print('13 的位反转,其中 ndarray 的 dtype 是 uint8:')
print(np.invert(np.array([13], dtype = np.uint8)))
# [242]
# 比较 13 和 242 的二进制表示,我们发现了位的反转
print('13 的二进制表示:')
print(np.binary_repr(13, width = 8))
# 00001101
print('242 的二进制表示:')
print(np.binary_repr(242, width = 8))
# 11110010
请注意,np.binary_repr()函数返回给定宽度中十进制数的二进制表示。
4. left_shift
numpy.left shift()函数将数组元素的二进制表示中的位向左移动到指定位置,右侧附加相等数量的 0。
例子
import numpy as np
print('将 10 左移两位:')
print(np.left_shift(10,2))
# 40
print('10 的二进制表示:')
print(np.binary_repr(10, width = 8))
# 00001010
print('40 的二进制表示:')
print(np.binary_repr(40, width = 8))
# 00101000
# '00001010' 中的两位移动到了左边,并在右边添加了两个 0。
5.right_shift
numpy.right_shift()函数将数组元素的二进制表示中的位向右移动到指定位置,左侧附加相等数量的 0。
例子
import numpy as np
print('将 40 右移两位:')
print(np.right_shift(40,2))
# 10
print('40 的二进制表示:')
print(np.binary_repr(40, width = 8))
# 00101000
print('10 的二进制表示:')
print(np.binary_repr(10, width = 8))
# 00001010
# '00001010' 中的两位移动到了右边,并在左边添加了两个 0。