NumPy是一个在Python中做科学计算的核心库,它提供了一个高性能的多维数组对象,以及用于处理这些数组的工具。
一、NumPy数据结构:数组(Arrays)
1、创建数组
NumPy数组是一个值网格,所有类型都相同,并由非负整数元组索引。数组的形状是一个整数元组,给出了每个维度的数组大小。
import numpy as np
# 按照给定数据生成普通数组
a1 = np.array([1, 2, 3])
a2 = np.array([[1,2,3],[4,5,6]])
print(a1.shape)
# 生成全0数组
c = np.zeros((2,2))
# 生成全1数组
d = np.ones((1,2))
# 生成全为指定值的数组
e1 = np.full((2,2), 7)
e2 = np.full((3, 3), True, dtype=bool) # 创建一个布尔数组
# 生成指定维数的单位矩阵
f = np.eye(2)
# 生成用0-1之间随机数填充的数组
g = np.random.random((2,2))
# 创建指定规律的数组
b1 = np.arange(20) # 一维的长度20的数组,值范围为0到19
b2 = np.arange(2, 10) # 值范围2到10
b3 = np.arange(2, 3, 0.1) # 值范围2到3,每次递增0.1
b4 = np.linspace(1,4,6) # 创建具有指定数量元素的数组,并在指定的开始值和结束值之间平均间隔
2、数组索引
NumPy提供了几种索引数组的方法。
(1)整数枚举索引
import numpy as np
a = np.array([[1,2,3,4],
[5,6,7,8],
[9,10,11,12]])
# 整数枚举索引
print(a[1, 2])
# 7
print(a[[0,1,2], [0,0,0]]) # 对应组合
# [1,5,9]
print(a[[0,1,2], [0,1,0]])
# [1,6,9]
整数枚举索引的一个有用技巧是从矩阵的每一行中选择或改变一个元素。
import numpy as np
a = np.array([[1,2,3,4],
[5,6,7,8],
[9,10,11,12]])
b = np.array([0, 2, 1])
print(a[np.arange(3), b])
# [1 7 10]
a[np.arange(3), b] += 10
print(a)
(2)布尔索引
通常,这种类型的索引用于选择满足某些条件的数组元素。
import numpy as np
a = np.array([[1,2,3,4],
[5,6,7,8],
[9,10,11,12]])
bool_idx = (a > 2)
print(bool_idx)
# [[False False True True]
# [ True True True True]
# [ True True True True]]
print(a[bool_idx])
# [3 4 5 6 7 8 9 10 11 12]
print(a[a > 2])
# [3 4 5 6 7 8 9 10 11 12]
(3)切片索引
我们可以对NumPy数组进行切片。由于数组可能是多维的,因此必须为数组的每个维指定一个切片。
import numpy as np
a = np.array([[1,2,3,4],
[5,6,7,8],
[9,10,11,12]])
# 序号为2的行之前(不包含)
# 序号为1的列及其之后(包含),序号为3的列之前(不包含)
b = a[:2, 1:3]
print(b)
# np.array([[2,3],
# [6,7]])
还可以将整数索引与切片索引混合使用。 但是,这样做会产生比原始数组更低级别的数组。
import numpy as np
a = np.array([[1,2,3,4],
[5,6,7,8],
[9,10,11,12]])
b1 = a[1, :]
print(b1)
# [5,6,7,8]
b2 = a[1:2, :]
print(b2)
# [[5,6,7,8]]
3、数组 I/O
NumPy 可以读写磁盘上的文本数据或二进制数据。
(1)numpy.save()+numpy.load()
NumPy 为 ndarray 对象引入了一个简单的文件格式:npy。npy 文件用于存储重建 ndarray 所需的数据、图形、dtype 和其他信息。
import numpy as np
a = np.array([1,2,3,4,5])
# 保存到 outfile.npy 文件上
np.save('outfile.npy',a)
# 如果文件路径末尾没有扩展名 .npy,该扩展名会被自动加上
np.save('outfile',a)
# 读取.npy文件
b = np.load('outfile.npy')
print(b)
(2)numpy.savze()+numpy.load()
numpy.savez() 函数将多个数组保存到以 npz 为扩展名的文件中。
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
b = np.arange(0, 1.0, 0.1)
c = np.sin(b)
# 保存到 outfile.npz 文件上, c 使用了关键字参数 sin_array
np.savez("outfile.npz", a, b, sin_array = c)
# 读取.npz文件
z = np.load("outfile.npz")
print(z)
(3)numpy.savetxt()+numpy.loadtxt()
savetxt() 函数是以简单的文本文件格式存储数据,对应的使用 loadtxt() 函数来获取数据。
import numpy as np
a = np.arange(0,10,0.5).reshape(4,-1)
# 保存到 outfile.txt 文件上, 以逗号分隔
np.savetxt("outfile.txt", a, delimiter=",")
# 读取 outfile.txt 文件,指定为逗号分隔
b = np.loadtxt("outfile.txt", delimiter=",")
print(b)
二、数组(Arrays)的基本操作
1、添加删除元素
(1)在末尾添加元素
append 函数在数组的末尾添加值。如果未提供轴,则输入数组会被展开。
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
print(np.append(a, [7,8,9]))
# [1 2 3 4 5 6 7 8 9]
# 横向追加
print(np.append(a, [[5,5,5],[7,8,9]], axis = 1))
# [[1 2 3 5 5 5]
# [4 5 6 7 8 9]]
# 纵向追加
print(np.append(a, [[7,8,9]], axis = 0))
# [[1 2 3]
# [4 5 6]
# [7 8 9]]
(2)在指定位置插入元素
insert 函数在给定索引之前,沿给定轴在输入数组中插入值。如果未提供轴,则输入数组会被展开。
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
print(np.insert(a, 2, [7,8,9]))
# [1 2 7 8 9 3 4 5 6]
# 横向插入
print(np.insert(a, 1, [11], axis = 1))
# [[ 1 11 2 3]
# [ 4 11 5 6]]
# 纵向插入
print(np.insert(a, 1, [11], axis = 0))
# [[ 1 2 3]
# [11 11 11]
# [ 4 5 6]]
(3)删除特定元素
delete 函数用于返回从输入数组中删除指定索引的新数组。如果未提供轴参数,则输入数组将展开。
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
print(np.delete(a, 5))
# [1 2 3 4 5]
# 删除行
print(np.delete(a, 1, axis = 0))
# [[1 2 3]]
# 删除列
print(np.delete(a, 1, axis = 1))
# [[1 3]
# [4 6]]
(4)删除重复元素
unique 函数用于去除数组中的重复元素。
import numpy as np
a = np.array([5,2,6,2,7,5,6,8,2,9])
# 去重后数组
print(np.unique(a))
# 返回去重数组的索引数组
u, indices = np.unique(a, return_index = True)
print(u, indices)
# 返回去重数组的下标
u, indices = np.unique(a,return_inverse = True)
print(u, indices)
# 返回去重元素重复数量
u, indices = np.unique(a, return_counts = True)
print(u, indices)
2、变换
(1)修改形状
reshape 函数有返回值,可以在不改变原数据的条件下修改形状。
resize 函数无返回值,会改变原数组的形状。
import numpy as np
a = np.arange(8)
print(a)
# [0 1 2 3 4 5 6 7]
b = a.reshape(4,2)
print(b)
# [[0 1]
# [2 3]
# [4 5]
# [6 7]]
b.resize(2,4)
print(b)
# [[0 1 2 3]
# [4 5 6 7]]
(2)压平
ravel(散开,解开),flatten(变平),两者所要实现的功能是一致的(将多维数组降位一维)。
import numpy as np
a = np.arange(8).reshape(2,4)
print(a)
# [[0 1 2 3]
# [4 5 6 7]]
print(a.flatten())
# [0 1 2 3 4 5 6 7]
print(a.flatten(order = 'F'))
# [0 4 1 5 2 6 3 7]
print(a.ravel())
# [0 1 2 3 4 5 6 7]
print(a.ravel(order = 'F'))
# [0 4 1 5 2 6 3 7]
区别在于返回拷贝(copy)还是返回视图(view),flatten()返回的是一份拷贝,对拷贝所做的修改不会影响原始矩阵,而ravel()返回的是视图,会影响原始矩阵。
import numpy as np
a = np.arange(8).reshape(2,4)
a.flatten()[1] = 10
print(a)
# [[0 1 2 3]
# [4 5 6 7]]
a.ravel()[1] = 10
print(a)
# [[ 0 10 2 3]
# [ 4 5 6 7]]
(3)转置
在处理一、二维数组时,ndarray.T 和 transpose()都可以实现矩阵转置效果。
import numpy as np
a = np.arange(12).reshape(3,4)
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print(np.transpose(a))
# [[ 0 4 8]
# [ 1 5 9]
# [ 2 6 10]
# [ 3 7 11]]
print(a.T)
# [[ 0 4 8]
# [ 1 5 9]
# [ 2 6 10]
# [ 3 7 11]]
对于高维数组,需要用到 transpose() 和一个由轴编号组成的元组,才能进行转置。
import numpy as np
a = np.arange(12).reshape(2,2,3)
print(a.shape) # 返回形状为(2, 2, 3), 可以对该元组进行索引, 也就是0,1,2
# 转置
print(a.transpose((1,0,2)))
(4)轴对换
swapaxes() 它接受一对轴编号,进行轴对换。
import numpy as np
a = np.arange(12).reshape(2,2,3)
print(a.shape) # 返回形状为(2, 2, 3), 可以对该元组进行索引, 也就是0,1,2
# 交换0、1轴
print(a.swapaxes(1,0))
print(a.swapaxes(0,1))
# 交换0、2轴
print(a.swapaxes(0,2))
print(a.swapaxes(2,0))
2、修改数组维度
(1)增加维度
import numpy as np
a = np.arange(9).reshape(3,3)
print(a.shape)
# (3, 3)
b1 = np.expand_dims(a, axis = 0)
print(b1.shape)
# (1, 3, 3)
b2 = np.expand_dims(a, axis = 1)
print(b2.shape)
# (3, 1, 3)
(2)删除一维条目
import numpy as np
a = np.arange(9).reshape(1,3,3)
print(a.shape)
# (1, 3, 3)
print(a)
# [[[0 1 2]
# [3 4 5]
# [6 7 8]]]
b = np.squeeze(a)
print(b.shape)
# (3, 3)
print(b)
# [[0 1 2]
# [3 4 5]
# [6 7 8]]
3、数组合并与拆分
(1)hstack()、vstack()、concatenate()
hstack()用于横向堆叠序列中的数组,vstack()用于纵向堆叠序列中的数组。concatenate()支持按照指定轴堆叠数组序列,可实现前两者的功能。
import numpy as np
a = np.array([[1, 2],
[3, 4]])
b = np.array([[5, 6],
[7, 8]])
# 横向堆叠
h1 = np.hstack((a, b))
h2 = np.concatenate((a,b), axis = 1)
print(h1)
print(h2)
# [[1 2 5 6]
# [3 4 7 8]]
# 纵向堆叠
v1 = np.vstack((a, b.T))
v2 = np.concatenate((a,b), axis = 0)
print(v1)
print(v2)
# [[1 2]
# [3 4]
# [5 7]
# [6 8]]
(2)hsplit()、vsplit()、split()
hsplit()指定需要分裂的array数量对原array进行水平分裂,vsplit()需要分裂的array数量对原array进行垂直分裂。split()支持按照指定轴分裂数组序列,可实现前两者的功能。
import numpy as np
a = np.arange(16).reshape(4,4)
print(a)
# 横向分割
print(np.hsplit(a, 2))
print(np.split(a, 2, axis = 1))
# 纵向分割
print(np.vsplit(a, 2))
print(np.split(a, 2, axis = 0))
3、排序
(1)numpy.sort()
numpy.sort() 函数返回输入数组的排序副本。
import numpy as np
a = np.array([[6,2],
[1,4]])
print(np.sort(a, axis=0, kind='quicksort'))
# axis=0按照列排序, axis=1按照行排序
# kind可选'quicksort'(快速排序)、'mergesort'(归并排序)、'heapsort'(堆排序)
(2)numpy.argsort()
numpy.argsort() 函数返回的是数组值从小到大的索引值。
import numpy as np
a = np.array([5, 1, 7])
idx = np.argsort(a)
print(idx)
print(a[idx])
(3)numpy.lexsort()
numpy.lexsort() 用于对多个序列进行排序。把它想象成对电子表格进行排序,每一列代表一个序列,排序时优先照顾靠后的列。
这里举一个应用场景:小升初考试,重点班录取学生按照总成绩录取。在总成绩相同时,数学成绩高的优先录取,在总成绩和数学成绩都相同时,按照英语成绩录取…… 这里,总成绩排在电子表格的最后一列,数学成绩在倒数第二列,英语成绩在倒数第三列。
import numpy as np
a = np.array([1, 5, 7])
b = np.array([2, 1, 5])
ind = np.lexsort((a,b))
ind2 = np.lexsort((b,a))
print(ind)
print([str(a[i])+","+str(b[i]) for i in ind])
print(ind2)
print([str(a[i])+","+str(b[i]) for i in ind2])
4、搜索
(1)argmax() 、argmin()
argmax() 和 argmin()分别沿给定轴返回最大和最小元素的索引。
import numpy as np
a = np.array([[30,40,70],
[80,20,10],
[50,90,60]])
print(np.argmax(a))
print(np.argmax(a.flatten()))
# 7
print(np.argmin(a))
print(np.argmin(a.flatten()))
# 5
# 横向的最大最小索引
print(np.argmax(a, axis = 1))
print(np.argmin(a, axis = 1))
# 纵向的最大最小索引
print(np.argmax(a, axis = 0))
print(np.argmin(a, axis = 0))
(2)nonzero()
nonzero() 返回数组非零元素的索引。
import numpy as np
a = np.array([[0,40,70],
[80,0,10],
[50,90,0]])
idx = np.nonzero(a)
print(idx)
print(a[idx])
(3)where()、extract()
where() 函数返回输入数组中满足给定条件的元素的索引。
extract() 函数是直接返回满足条件的元素。
import numpy as np
a = np.array([[0,40,70],
[80,0,10],
[50,90,0]])
idx = np.where(a > 30)
print(a[idx])
condition = a > 30
print(np.extract(condition, a))
三、数组(Arrays)的算术运算
1、加减乘除
简单的加减乘除: add(),subtract(),multiply() 和 divide()。
import numpy as np
a = np.arange(9).reshape(3,3)
b = np.array([10,10,10])
print(np.add(a,b))
# [[10 11 12]
# [13 14 15]
# [16 17 18]]
print(np.subtract(a,b))
# [[-10 -9 -8]
# [ -7 -6 -5]
# [ -4 -3 -2]]
print(np.multiply(a,b))
# [[ 0 10 20]
# [30 40 50]
# [60 70 80]]
print(np.divide(a,b))
# [[0. 0.1 0.2]
# [0.3 0.4 0.5]
# [0.6 0.7 0.8]]
2、倒数、幂和余数
import numpy as np
a = np.array([0.25, 1.33, 1, 100])
# 倒数
print(np.reciprocal(a))
# 幂
b = np.array([1,2,3,2])
print(np.power(a, 2))
print(np.power(a, b))
# 余数
print(np.mod(a, b))
3、三角函数
import numpy as np
a = np.array([0,30,45,60,90])
# 通过乘 pi/180 转化为弧度
print(np.sin(a*np.pi/180))
print(np.cos(a*np.pi/180))
print(np.tan(a*np.pi/180))
4、指数和对数
import numpy as np
a = np.array([1,30,45,60,90])
# 计算逐个元素的指数
print(np.exp(a))
# 计算逐个元素的指数 -1
print(np.expm1(a))
# 计算逐个元素的自然对数
print(np.log(a))
# 计算逐个元素的以10为底的对数
print(np.log10(a))
5、求和、求乘积
import numpy as np
a = np.array([1,30,45,60,90])
# 各元素的乘积
print(np.prod(a))
# 各元素的和
print(np.sum(a))
6、位运算
import numpy as np
# 数字的二进制表示
print(np.binary_repr(12, width = 8))
print(bin(12))
# 按位与
print(np.bitwise_and(12, 6))
# 按位或
print(np.bitwise_or(12, 6))
# 位取反运算
print(np.invert(np.array(12, dtype = np.uint8)))
四、数组(Arrays)的统计运算
1、最小值、最大值
amin() 用于计算数组中的元素沿指定轴的最小值。
amax() 用于计算数组中的元素沿指定轴的最大值。
import numpy as np
a = np.array([[3,7,5],
[8,4,3],
[2,4,9]])
# 横向最小
print(np.amin(a, 1))
# [3 3 2]
# 纵向最小
print(np.amin(a, 0))
# [2 4 3]
# 横向最大
print(np.amax(a, 1))
# [7 8 9]
# 纵向最大
print(np.amax(a, 0))
# [8 7 9]
2、中位数、分位数、平均数、加权平均值
median() 函数用于计算数组中元素的中位数(中值)
percentile() 函数用于计算数组中元素的百分位数
mean() 函数返回数组中元素的算术平均值。 如果提供了轴,则沿其计算。
average() 函数根据在另一个数组中给出的各自的权重计算数组中元素的加权平均值。该函数可以接受一个轴参数。 如果没有指定轴,则数组会被展开。
import numpy as np
a = np.array([[30,65,70],
[80,95,10],
[50,90,60]])
print(np.median(a))
print(np.median(a, axis = 0))
print(np.percentile(a, 50))
print(np.percentile(a, 50, axis=0))
print(np.mean(a))
print(np.mean(a, axis = 0))
b = np.array([1,2,3,4])
wts = np.array([4,3,2,1])
print(np.average(b))
print(np.average(b, weights = wts))
3、标准差、方差
import numpy as np
print(np.std([1,2,3,4]))
print(np.var([1,2,3,4]))
五、数组(Arrays)与线性代数
NumPy 提供了线性代数函数库 linalg,该库包含了线性代数所需的所有功能。
1、范数
范数是对向量(或者矩阵)的度量,是一个标量(scalar)
import numpy as np
a = np.array([3, 4])
# 二范数 5
print(np.linalg.norm(a))
print(np.linalg.norm(a, ord=2))
# 一范数 7
print(np.linalg.norm(a, ord=1))
2、内积和外积
import numpy as np
# 内积
a = np.array([[0.0, -1.0],
[1.0, 0.0]])
print(np.dot(a, a))
# 外积
b = np.arange(1,6)
print(np.multiply.outer(b, b))
3、逆矩阵
import numpy as np
a = np.array([[1, 2],
[-1,-3]])
print(np.linalg.inv(a))
4、特征值
import numpy as np
a = np.array([[1, 2, 3],
[-1,-3, 1]])
# 先进行矩阵的乘法,把矩阵和矩阵的转置相乘,得到一个方阵
U = np.dot(a, a.T)
# 把方阵作为输入,可以得到特征值和特征向量
lamda,hU = np.linalg.eig(U)
sigma = np.sqrt(lamda)
print(sigma)
# 用奇异值分解验证
Q,S,VT = np.linalg.svd(a)
print(S)