Python科学计算模块NumPy学习笔记

编程 · 2023-08-16 · 209 人浏览

基本介绍

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()传入的关键字参数:

  1. fname:文件名,数据类型为字符串
  2. delimiter:分隔符,数据类型为字符串
  3. usecols:读取列数,数据类型为元组, 其中元素个数有多少个,则选出多少列
  4. 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]
Python
Theme Jasmine by Kent Liao