Numpy是一个常用的Python科学技术库,通过它可以快速对数组进行操作,包括形状操作、排序、选择、输入输出、离散傅立叶变换、基本线性代数,基本统计运算和随机模拟等。许多Python库和科学计算的软件包都使用Numpy数组作为操作对象,或者将传入的Python数组转化为Numpy数组,因此在Python中操作数据离不开Numpy。
Numpy的核心是ndarray
对象,由Python的n维数组封装而来,但通过C语言预编译相关的数组操作,因此比原生Python具有更高的执行效率,但仍然使用Python语言编码,这样就同时具有简洁的代码和高效的运行速度。ndarry与数组有些区别值得注意,numpy数组中的元素都具有相同的类型,并且在创建时就确定了固定的大小,这与Python数组对象可以动态增长不同。
Numpy对象的形式是同构多维数组,数组的维度称为轴(axis),每个维度上元素的个数称为轴的长度。例如下面是一个2×3的二维数组arr,第一轴长度为3,第二轴长度为2
arr = [[ 1., 0., 0.],
[ 0., 1., 2.]]
arr数组对象常用的属性如下:
# 数组轴的个数
arr.ndim
# 数组维度及长度,例如2×3的数组其shape为(2, 3)
arr.shape
# 数组元素的总个数
arr.size
# 数组中元素的数据类型
arr.dtype
# 数组中元素所占字节数
arr.itemsize
可以通过array()
方法包裹普通python数组将其转化为numpy数组,通过dtype=
规定元素的数据类型。数组可以是二维等高维数组,也可以是元组的形式。
填充指定大小的数组可以使用函数zeros()
,将元素都填充为0,或者ones()
将元素填充为1,empty()
将元素填充为随机数
np.random
模块用于产生随机数,rand()
创建[0,1)内指定维度的随机数组,uniform()
、randint()
在指定范围内生成浮点数/整数。有时候我们希望产生的相同的随机数,或者产生的随机数可以再现,可以通过np.random.seed()
指定随机数种子,当种子相同时产生的随机数相同。
arange(a,b,c)
函数用于从a到b每隔c长度生成一个数组元素。linspace(a,b,c)
函数用于在a到b之间生成c个数组元素
import numpy as np
# 普通数组转化为numpy数组
a1 = np.array([2, 3, 4], dtype=float)
print(a1)
# 将元组数组转化为二维numpy数组
a2 = np.array([(1, 2, 3), (3, 4, 5)])
print(a2)
# 将3×3的数组用1填充
a3 = np.ones((3, 3))
print(a3)
# 从1到10,每隔2生成一个元素
a4 = np.arange(1, 10, 2)
print(a4)
# 在1到12之间生成4个元素
a5 = np.linspace(1, 12, 4, dtype=int)
print(a5)
# 生成指定维度的随机数组
print(np.random.rand(1, 5))
# 在指定区间生成随机浮点数
print(np.random.uniform(low=1, high=2, size=5))
# 在指定区间生成随机浮点数
print(np.random.randint(low=1, high=10, size=5))
# 使用相同的种子产生随机数
np.random.seed(666)
print(np.random.rand(5))
np.random.seed(666) # 每次产生前都要设置
print(np.random.rand(5))
'''
普通数组转化为numpy对象:
[2. 3. 4.]
元组数组:
[[1 2 3]
[3 4 5]]
用1填充数组:
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]
从1到10每隔2生成一个元素:
[1 3 5 7 9]
在1到12之间生成4个元素:
[ 1 4 8 12]
生成(1, 5)浮点数组
[[0.73483009 0.42395071 0.03067476 0.87905934 0.3825438 ]]
在[1,2)产生5个随机浮点数数
[1.73563967 1.20877954 1.59162307 1.1021683 1.29736842]
在[1,10)产生5个随机整数
[6 5 3 6 8]
相同的随机数
[0.70043712 0.84418664 0.67651434 0.72785806 0.95145796]
[0.70043712 0.84418664 0.67651434 0.72785806 0.95145796]
'''
算术运算符可以直接运用在矩阵上,其结果是将运算应用到每个元素上,例如矩阵A*B就是每个元素对应相乘,矩阵的乘法运算使用的是@符号
A = np.array([[1, 1],
[0, 1]])
B = np.array([[2, 0],
[3, 4]])
print(A * B)
print(A @ B)
'''
矩阵元素对应相乘:
[[2 0]
[0 4]]
矩阵的乘法:
[[5 4]
[3 4]]
'''
numpy中有些函数应用于整个数组,例如求和sum
、最大值max
、最小值min
。如果在这些参数中指定了某个轴,则应用于指定轴。
还有一些函数应用于数组中的具体元素,例如求sin
、cos
、exp
、开方sqrt
等,这些函数叫做通函数(ufunc)
a = np.array([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]])
print(a.max()) # 求整体的最大值,结果为11
print(a.sum(axis=0)) # 求每一列的和,结果为:[12 15 18 21]
print(np.sqrt(a)) # 数组每个元素求开方
numpy中的数组同python中的list一样可以进行索引、切片和迭代操作。a[x]
代表访问数组a下标为x的元素,a[x:y]
代表访问数组从x到y的元素,如果省略x代表从头开始,省略y代表直到结尾。a[x:y:a]
代表从x到y每隔a个元素取一个值,如果a为负数,代表逆序取值。
a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(a[1:3]) # 输出下标为1到3的元素:[1 2]
print(a[::-2]) # 逆序每隔两个元素选一个值:[9 7 5 3 1]
如果是多维数组,则索引之间用逗号分隔。可以使用…代表省略某几个维度,如果省略则会被认为是该维度全部输出,例如x[...,3]
等效于 x[:,:,:,:,3]。
可以通过for
循环迭代多为数组,其内容为低一维度的子数组,如果希望遍历每一个子元素,可以使用flat
属性。
a = np.array([[0, 1, 2, 3],
[10, 11, 12, 13],
[40, 41, 42, 43]])
# 输出a第一维(行)的前三个,第二维(列)下标的1~3
print(a[1:3, 0:3])
# 输出行的所有,下标为2的列
print(a[2, ...])
# 遍历数组
for row in a:
print(row)
# 遍历每个子元素
for item in a.flat:
print(item)
'''
后两行的1~3列:
[[10 11 12]
[40 41 42]]
第三行的所有列:
[40 41 42 43]
遍历数组:
[0 1 2 3]
[10 11 12 13]
[40 41 42 43]
遍历每个元素:
0
1
2
......
41
42
43
'''
除了使用具体数字作为索引,还可以使用numpy数组作为索引。例如当索引i为多维数组时,将会按照i的位置从数组a中取出元素
a = np.arange(12) ** 2
print(a)
i = np.array([1, 3, 5])
print(a[i])
# 多维数组索引j
j = np.array([[3, 4], [9, 7]])
print(a[j])
'''
[ 0 1 4 9 16 25 36 49 64 81 100 121]
数组a的1、3、5个元素
[ 1 9 25]
通过多为索引j取出a的数据填到对应位置
[[ 9 16]
[81 49]]
'''
当索引数组由单个元素组成时,默认对数组a的第一个维度进行选择。若需要对数组在多个维度上进行索引,则传入多个索引组成的数组i,j并用逗号分隔
a = np.array(([[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]]))
# 对a的第一个维度进行选择
i = np.array([0, 1])
print(a[i])
# 对数组a在多个维度上进行选择,同时提供i,j代表取出a的[0,2]、[1,3]两个元素
j = np.array([2, 3])
print(a[i, j])
'''
选择多维数组a的第0、1两行:
[[0 1 2 3]
[4 5 6 7]]
a的[0,2]、[1,3]两个元素:
[2 7]
'''
还可以通过True/False数组代表元素是否被选中,例如下面有一个True/False数组mask,新建一个全为0的2×2数组padded,然后使用一个数组序列[1,2,3]去填充padded,可见mask为True的地方被选择并填充,False的地方未被选中,仍为0.
mask = np.array([[True, False],
[True, True]])
padded = np.zeros((2, 2))
padded[mask] = np.array([1, 2, 3]) # 用序列填充被mask选中的位置
print(padded)
'''
[[1. 0.]
[2. 3.]]
'''
数组的reshape()
方法可以将原数组重构成目标维度的数组,例如将一个2×6的数组重构为3×4的数组,
数组在重构时不会修改原数组,而是返回修改后的结果数组
值得注意的是数组在重构和打印时都是从最右边的维度开始往左进行,例如下面的3×4的数组b,先按行排列4个,然后再换行,排列这样的3行。如果是多维,则按这样的行列继续输出。如果数组维度为-1,则会自动计算该维度的大小,例如含有12个元素的数组,第二、第三维是3×2,则第一维就是2
ravel()
函数可以将数组展成一维数组。
a=np.array([[1,2,3,4,5,6],
[7,8,9,10,11,12]])
b=a.reshape(3,4)
print(b)
# 多维数组,自动计算
print(a.reshape(-1,3,2))
# 展开数组
flatted = b.ravel()
print(flatted, end=' ')
'''
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
2×3×2的多维数组:
[[[ 1 2]
[ 3 4]
[ 5 6]]
[[ 7 8]
[ 9 10]
[11 12]]]
展开数组:
[ 1 2 3 4 5 6 7 8 9 10 11 12]
'''
transpose()
函数可以实现数组转置的功能,例如下面数组a为一个4×3×2的三维数组,通过transpose(2,0,1),其中的0、1、2分别代表数组原来的维度第一、二、三维,在括号中的位置代表转置后的维度,因此结果将数组的第三维放到第一维,第一维放到第二维,第二维放到第三维,即转置为3×2×4的数组。
a = np.array([
[[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]],
[[13, 14, 15, 16],
[17, 18, 19, 20],
[21, 22, 23, 24]]
])
b = a.transpose(2, 0, 1)
print(b)
'''
[[[ 1 5 9]
[13 17 21]]
[[ 2 6 10]
[14 18 22]]
[[ 3 7 11]
[15 19 23]]
[[ 4 8 12]
[16 20 24]]]
'''
numpy的concatenate()
可以用于数组的合并,其第一个参数为要合并的数组,放在一个tuple()或list[]中。第二个参数axis
指定合并的轴,默认axis=0,即从第一维合并所有子元素
a1 = np.array([[1, 3], [5, 7]])
a2 = np.array([[2, 4], [6, 8]])
a3 = np.concatenate((a1, a2)) # 合并数组
print(a3)
print(np.concatenate((a1, a2), axis=1)) # 指定合并的轴
'''
[[1 3]
[5 7]
[2 4]
[6 8]]
[[1 3 2 4]
[5 7 6 8]]
'''
numpy的hstack()
函数可以在水平方向上合并多个数组,vstack()
函数可以在垂直方向上合并多个数组
相反地,hsplit()
、vsplit()
可以拆分为指定数量的数组
a=np.array([1,2,3])
b=np.array([4,5,6])
# 垂直方向合并
c=np.vstack((a,b))
print(c)
# 水平方向合并
print(np.hstack((a,b)))
# 水平方向拆分
print(np.hsplit(c,3))
'''
垂直堆叠
[[1 2 3]
[4 5 6]]
水平合并
[1 2 3 4 5 6]
水平拆分为三个1×2的:
[array([[1],
[4]]),
array([[2],
[5]]),
array([[3],
[6]])]
'''
当一个数组对象赋值给一个新的变量时,是新开辟一个存储空间还是只是传递一个引用?答案是引用。
例如执行语句b=a,只是将一个引用传给了b,对b执行的操作会直接影响a。查看a、b两个对象的节点id也是一样的
a = np.array([1, 2, 3])
b = a
# 修改b
b[0] = 0
print(a)
# 输出a、b对象的id
print(id(a), id(b))
'''
修改b,a也发生了变化
[0 2 3]
查看二者的id
2290013812656 2290013812656
'''
通过切片返回数组的视图,修改视图的形状不会影响原数组,但是在视图上修改数据原数组也会改变。在执行del a之后,由于c引用了a,a依旧会存在内存中不会被删除
c = a[:]
# 修改视图的形状
c.shape = 3, 1
print(c, a)
# 修改视图c的数据
c[0] = 1
print(a[0])
'''
对视图c的形状做修改,a不会受到影响
[[0]
[2]
[3]] [0 2 3]
修改c的数据,a也会随之改变:
1
'''
通过copy()
方法可以生成数据的副本,对副本的操作完全不会影响原数组
d= a.copy()
d[0]=5
# 修改数组的副本d,a不受影响,输出a:[1 2 3]
print(a)
numpy通过save()
将一个数组arr保存为.npy文件,并且可以通过np.load()
读取该文件
如果npy文件中存在序列化的pickled对象,则需要设置属性allow_pickle=True
,否则会报错ValueError: Object arrays cannot be loaded when allow_pickle=False
np.save("output.npy",arr)
arr = np.load("output.npy", allow_pickle=True)
如果需要储存多个数组,可以通过np.savez()
保存为.npz文件,其变量名作为数组的key值,同理可以使用load()
读取
np.savez('array_save.npz',arr1,arr2,arr3)
Arr=np.load('arr_save.npz')
arr1=Arr['arr1'] # 通过key值取到不同数组
.npy与.npz文件无法手动查看,如果需要查看可以保存为txt文本的格式,通过savetxt()
保存,并通过loadtxt()
读取
np.savetxt('data.txt',arr)
data=np.loadtxt('data.txt')
在保存文件时可以通过fmt
属性设置输出数组的格式,如下设置保存数据为整型,且后跟一个制表符
np.savetxt("./output.txt", data, fmt='%d ')