基本介绍
NumPy:Numerical Python的缩写,提供了底层基于C语言实现的数值计算库,与Python内置的list数据结构相比,其支持更加规范的数据类型和极其丰富的操作接口,速度也更快。
安装NumPy
NumPy是第三方库,所以在使用前必须先安装NumPy。可以使用pip命令安装:pip install numpy
。
安装完就可以写NumPy代码了。一般流程是先import numpy
,为了后续调用方便,通常在import完之后,还给它一个缩写形式as np
。
创建NumPy数组ndarray
NumPy默认ndarray所有元素的类型是相同的。如果传进来的列表中包含不同的类型,则统一为同一类型,优先级:str > float > int。
使用np.array()
import numpy as np
n = np.array([1, 2, 3])
print(n.shape) # 形状:(3,)
使用np.ones()
创建一个所有元素都为1的多维数组。
n = np.ones((3, 2), dtype=np.int8) # 默认是float类型
print(n.shape) # 形状:(3, 2)
创建一个所有元素都为0的多维数组,使用np.zeros()。
创建一个所有元素都为指定元素的多维数组,使用np.full():
n = np.full((3, 2), fill_value=6)
print(n)
# 输出:
# [[6 6]
# [6 6]
# [6 6]]
使用np.eye()
创建一个单位矩阵:主对角线都是1,其他都是0的二维数组。
n = np.eye(5, 5, dtype=np.int8)
print(n)
# 输出:
# [[1 0 0 0 0]
# [0 1 0 0 0]
# [0 0 1 0 0]
# [0 0 0 1 0]
# [0 0 0 0 1]]
# k=1向右偏移1个位置,k=-1向左偏移1个位置
n = np.eye(5, 5, k=1, dtype=np.int8)
print(n)
# [[0 1 0 0 0]
# [0 0 1 0 0]
# [0 0 0 1 0]
# [0 0 0 0 1]
# [0 0 0 0 0]]
使用np.linspace()
创建一个等差数列。参数说明:
- start: 开始值
- stop: 结束值
- num=50: 等差数列中默认有50个数
- endpoint=True: 是否包含结束值
- retstep=False: 是否返回等差值(步长)
- dtype=None: 元素类型
n = np.linspace(0, 10, num=6, dtype=np.int8)
print(n) # [ 0 2 4 6 8 10]
使用np.arange()
创建一个数值范围的数组,和Python中的range功能类似。
n = np.arange(10)
print(n) # [0 1 2 3 4 5 6 7 8 9]
n = np.arange(2, 10)
print(n) # [2 3 4 5 6 7 8 9]
n = np.arange(2, 10, 2)
print(n) # [2 4 6 8]
使用np.random.randint()
创建一个随机整数的多维数组。
import numpy as np
import matplotlib.pyplot as plt
n = np.random.randint(3)
print(n) # 随机范围:[0, 3)
n = np.random.randint(3, 10)
print(n) # 随机范围:[3, 10)
n = np.random.randint(3, 10, size=5)
print(n) # 一维
n = np.random.randint(3, 10, size=(2, 3))
print(n) # 二维
n = np.random.randint(0, 256, size=(20, 40, 3))
plt.imshow(n) # 图像处理
plt.show() # 图像显示
使用np.random.randn()创建一个服从标准正态分布的多维数组:
n = np.random.randn()
print(n)
n = np.random.randn(5)
print(n)
n = np.random.randn(3, 4)
print(n)
使用np.random.normal()创建一个服从正态分布的多维数组:
n = np.random.normal(loc=10, scale=1, size=(2, 3))
print(n) # 均值:10,标准差:1,数组形状:2行3列
使用np.random.random()创建一个元素为[0, 1)随机数的多维数组:
n = np.random.random(size=(2, 3))
print(n)
使用np.random.rand()也是创建一个元素为[0, 1)随机数的多维数组,只是参数传递方式不同,random.rand()接收分开的参数。
两个函数功能完全一样,NumPy这么做可能是为了使MATLAB用户更容易学习Python+NumPy的组合。把其中一个函数去掉,所带来的麻烦远大于好处,因为有很多现存的代码使用了函数的不同版本。
数组属性
n = np.ones((3, 2), dtype=np.int8)
print(n.shape) # 形状:(3, 2)
print(n.ndim) # 维度:2
print(n.size) # 总数据量:6
print(n.dtype) # 元素类型:int8
基本操作
索引
n = np.array([1, 2, 3])
print(n[0], n[-1]) # 1 3
n = np.array([[1, 2, 3], [4, 5, 6]])
print(n[-1][-1], n[-1, -1]) # 6 6
切片
行切片:
n = np.array([1, 2, 3])
print(n[0:2]) # [1 2]
print(n[::-1]) # [3 2 1]
n = np.array([[1, 2], [3, 4], [5, 6]])
print(n[0]) # 取一行:[1 2]
print(n[0:2])
# 取连续行:
# [[1 2]
# [3 4]]
print(n[[0, 2]])
# 取不连续行:
# [[1 2]
# [5 6]]
列切片:
n = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(n[:, 0]) # 取一列,取所有行,第0列:[1 4 7]
print(n[:, 0:2])
# 取连续列:
# [[1 2]
# [4 5]
# [7 8]]
print(n[:, [0, 2]])
# 取不连续列:
# [[1 3]
# [4 6]
# [7 9]]
行翻转(上下翻转)和列翻转(左右翻转):
n = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(n[::-1])
# 行翻转
# [[7 8 9]
# [4 5 6]
# [1 2 3]]
print(n[:, ::-1])
# 列翻转
# [[3 2 1]
# [6 5 4]
# [9 8 7]]
数组变形
n = np.array([1, 2, 3, 4, 5, 6])
n2 = np.reshape(n, (2, 3)) # 2行3列
n3 = n.reshape((2, 3)) # 2行3列
n4 = n3.reshape(6) # 变成一维,也可以写成 reshape(-1)
n5 = n.reshape(2, -1) # -1表示任意剩余维度长度
数组合并
n1 = np.array([[1, 2, 3], [4, 5, 6]])
n2 = np.array([[7, 8, 9], [10, 11, 12]])
n3 = np.concatenate((n1, n2), axis=1) # 默认上下合并,axis=1左右合并
n4 = np.hstack((n1, n2)) # 水平合并,即左右合并
n5 = np.vstack((n1, n2)) # 垂直合并,即上下合并
数组拆分
n = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
n2 = np.vsplit(n, 3) # 垂直拆分,平均拆成3份,水平拆分 hsplit()
n3 = np.vsplit(n, (1,)) # 按照指定位置拆成
n4 = np.split(n, 3, axis=1) # 默认垂直拆分,axis=1水平合并
数组排序
- np.sort():不改变原数组,占内存空间
- ndarray.sort():改变原数组,不多占内存空间
n = np.array([2, 44, 5, 3, 8])
n2 = np.sort(n) # 默认axis=1按行排序,axis=0按列排序
n.sort()
print(n, n2)
拷贝
n = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
n2 = n.copy() # 深拷贝
聚合函数
n = np.array([[1, 2, 13], [4, 15, 6], [17, 8, 9]])
print("求和:", n.sum())
print("求和:", n.sum(axis=0)) # 每一列多行求和
n = np.array([1, 2, 3, np.nan])
print(np.nansum(n)) # 有nan(not a number)值时,sum无法求出和,要用nansum,它会排除掉nan之后再求和
print("最小值:", n.min())
print("最大值:", n.max())
print("平均值:", n.mean())
print("中位数:", np.median(n))
print("百分位数:", np.percentile(n, 25))
print("极差:", n.ptp())
print("第一个最大值下标:", n.argmax())
print("按条件找到所有最大值下标:", np.argwhere(n == n.max()))
print("3次方:", np.power(n, 3))
print("方差:", n.var())
print("标准差:", n.std())
print("对数:", np.log(n))
print("减法:", np.diff(n)) # 计算数组中相邻元素之间差值
print("符号:", np.sign(n)) # 值>0,返回1;值<0,返回-1;值=0,返回0
n = np.array([1, 2, 13])
weights = np.array([0.1, 0.2, 0.3])
print("加权平均:", np.average(n, weights=weights))
矩阵操作
算术运算
n = np.array([[11, 12, 13], [24, 15, 26], [17, 38, 39]])
print(n + 10) # 加
print(n - 10) # 减
print(n * 10) # 乘
print(n / 10) # 除
print(n // 10) # 整除
print(n % 2) # 余数
print(n**2) # 次方
线性代数
矩阵积:第一个矩阵的列数 = 第二个矩阵的行数。
n1 = np.array([[1, 2, 3], [4, 5, 6]])
n2 = np.array([[1, 2], [3, 4], [5, 6]])
print(np.dot(n1, n2))
其他矩阵操作:
n = np.array([[1, 2], [4, 5]])
print(np.linalg.inv(n)) # 矩阵逆
print(np.round(np.linalg.det(n))) # 矩阵行列式
print(np.linalg.matrix_rank(n)) # 矩阵的秩(满秩矩阵、奇异矩阵)
广播机制
ndarray广播机制的两条规则:
- 规则一:为缺失的维度补维度
- 规则二:缺失元素用已有值填充
n1 = np.ones((2, 3), dtype=np.int8)
n2 = np.arange(3)
print(n1 + n2)
# 输出
# [[1 2 3]
# [1 2 3]]
文件IO操作
数组保存和读取
NumPy可以用二进制的格式保存数据。如果不想让别人看到数据,想使用NumPy时加载的话,那完全可以用这种方式来保存数据。
使用save()来保存,保存的是一个以.npy结尾的二进制文件。加载的时候,用load()直接加载这个二进制数据文件。
row_string = "1, 10, 5, 2, 11, 22"
data = np.fromstring(row_string, dtype=np.int64, sep=",")
data = data.reshape(2, 3)
np.save("save_data.npy", data)
npy_data = np.load("save_data.npy")
print(npy_data)
NumPy还可以使用savez()将多个array保存到一个.npz文件中。
train_data = np.array([1, 2, 3])
test_data = np.array([11, 22, 33])
np.savez("save_data.npz", train=train_data, test=test_data)
npz_data = np.load("save_data.npz")
print("train:", npz_data["train"])
print("test:", npz_data["test"])
用savez()的时候,还有一个方法可以更节省空间,那就是用savez_compressed()来做一次数据压缩。
np.savez_compressed("save_data.npz", train=train_data, test=test_data)
文本文件读写操作
data = np.array([1, 10, 5, 2, 11, 22])
np.savetxt("save_data.csv", data, delimiter=",", fmt="%s")
使用np.loadtxt()就能自定义读取数据:
csv_data = np.loadtxt("save_data.csv", delimiter=",", dtype=np.int8)
print(csv_data)
loadtxt()传入的关键字参数:
- fname:文件名,数据类型为字符串
- delimiter:分隔符,数据类型为字符串
- usecols:读取列数,数据类型为元组, 其中元素个数有多少个,则选出多少列
- unpack:是否解包,数据类型为布尔。如果True,读入属性将分别写入不同变量
高级函数
分段函数
简单分段,可以使用where(condition, x, y),第一个参数表示条件,当条件成立时where方法返回x,当条件不成立时where返回y。
x = np.array([8, 9, -1, -6])
y = np.where(x > 0, x, np.abs(x))
print(y) # [8 9 1 6]
分段数较多,可以用select()。
x = np.array([8, 9, 0, -1, -6])
y = np.select([x < 0, x == 0, x > 0], [-1, 0, 1])
print(y) # [ 1 1 0 -1 -1]
where()、select()函数的所有参数都需要在调用它们之前完成计算,在计算时还会产生许多保存中间结果的数组,因此如果输入的数组很大,将会发生大量内存分配和释放。
piecewise()解决了上述问题,它只计算需要计算的值。piecewise()是专门用于计算分段的函数。
x = np.array([8, 9, 5, 0, -1, -6])
y = np.piecewise(x, [x < 0, x == 0, x > 0], [-1, 0, 1])
print(y) # [ 1 1 1 0 -1 -1]