第4章:NumPy数值计算基础
各位同学,今天我们来聊聊NumPy。说实话,我在储能行业摸爬滚打这么多年,NumPy是我每天都要打交道的工具。无论是处理电池测试数据,还是做SOC估算,都离不开它。这一章,我会把最核心的东西讲透。
4.1 NumPy数组创建
NumPy的核心就是ndarray,也就是N维数组。我习惯叫它「数组」。创建数组的方法有很多,但最常用的就几种。
4.1.1 从列表创建
import numpy as np
# 一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1) # [1 2 3 4 5]
# 二维数组
arr2 = np.array([[1, 2], [3, 4], [5, 6]])
print(arr2)
# [[1 2]
# [3 4]
# [5 6]]
嗯,这里要注意:传入的列表结构必须规整。我在项目中遇到过有人传了不规则嵌套列表,结果数组维度全乱了。
4.1.2 特殊数组
实际工作中,我们经常需要创建特定结构的数组。比如初始化权重、生成测试数据等。
# 全零数组
zeros = np.zeros((3, 4))
print(zeros)
# 全一数组
ones = np.ones((2, 3))
print(ones)
# 单位矩阵
eye = np.eye(4)
print(eye)
# 等差数列
arange = np.arange(0, 10, 2) # [0 2 4 6 8]
linspace = np.linspace(0, 1, 5) # [0. 0.25 0.5 0.75 1. ]
我的习惯:创建等差数列时,如果知道步长用arange,如果知道元素个数用linspace。别搞混了,否则数据点会少一个。
4.1.3 随机数组
随机数在储能数据分析中很常见,比如模拟噪声、初始化模型参数等。
# 均匀分布 [0,1)
rand_arr = np.random.rand(3, 3)
# 标准正态分布
randn_arr = np.random.randn(1000)
# 指定范围的随机整数
randint_arr = np.random.randint(0, 100, size=(5, 5))
4.2 数组索引与切片
索引和切片是操作数组的基础。说白了,就是怎么从数组里取数据。我刚开始学的时候,经常把维度搞混。
4.2.1 基本索引
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 取单个元素
print(arr[0, 1]) # 2
# 取整行
print(arr[1]) # [4 5 6]
# 取整列
print(arr[:, 1]) # [2 5 8]
你想想看,索引是从0开始的,这个和Python列表一样。但NumPy支持多维索引,用逗号分隔维度。
4.2.2 切片操作
# 取前两行
print(arr[:2])
# 取所有行的第1到第2列
print(arr[:, 1:3])
# 步长切片
print(arr[::2, ::2]) # 每隔一行取一行,每隔一列取一列
我曾经踩过的坑:切片返回的是原数组的视图,不是副本。修改切片会影响原数组。如果需要副本,用copy()方法。
4.2.3 花式索引
花式索引就是用数组作为索引。这在处理不规则数据时特别有用。
arr = np.arange(10)
indices = [1, 3, 5, 7]
print(arr[indices]) # [1 3 5 7]
# 二维花式索引
arr2d = np.arange(12).reshape(3, 4)
rows = [0, 2]
cols = [1, 3]
print(arr2d[rows, cols]) # [1 11]
4.3 数组运算
NumPy的数组运算非常高效,因为底层是C语言实现的。说白了,就是快。
4.3.1 算术运算
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 对应元素运算
print(a + b) # [5 7 9]
print(a * b) # [4 10 18]
print(a ** 2) # [1 4 9]
# 标量运算
print(a + 10) # [11 12 13]
这里有个关键点:广播机制。当两个数组形状不同时,NumPy会自动扩展维度。我在做电池SOC估算时,经常用广播来批量处理数据。
4.3.2 统计运算
data = np.random.randn(1000)
print(np.mean(data)) # 均值
print(np.std(data)) # 标准差
print(np.min(data)) # 最小值
print(np.max(data)) # 最大值
print(np.sum(data)) # 求和
# 沿指定轴计算
matrix = np.random.randn(3, 4)
print(np.mean(matrix, axis=0)) # 按列求均值
print(np.mean(matrix, axis=1)) # 按行求均值
重要提醒:axis参数很容易搞混。axis=0表示沿着行的方向操作,也就是跨行操作;axis=1表示沿着列的方向操作。我建议你多写几个例子验证一下。
4.3.3 矩阵运算
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# 矩阵乘法
C = np.dot(A, B)
# 或者用 @ 运算符
C = A @ B
# 转置
print(A.T)
# 逆矩阵
print(np.linalg.inv(A))
4.4 随机数生成
随机数在储能数据分析中应用广泛。比如蒙特卡洛模拟、数据增强、模型初始化等。
4.4.1 常用分布
# 设置随机种子,保证结果可复现
np.random.seed(42)
# 均匀分布
uniform = np.random.uniform(0, 1, 1000)
# 正态分布
normal = np.random.normal(0, 1, 1000)
# 二项分布
binomial = np.random.binomial(10, 0.5, 100)
# 指数分布
exponential = np.random.exponential(1, 1000)
我的经验:设置随机种子是个好习惯。我在做电池寿命预测时,如果不设种子,每次运行结果都不一样,根本没法调试。
4.4.2 随机抽样
# 从数组中随机选择
arr = np.arange(10)
sample = np.random.choice(arr, size=5, replace=False)
# 打乱数组顺序
np.random.shuffle(arr)
# 随机排列
perm = np.random.permutation(10)
知识体系总览
下面这张图,是我梳理的本章知识结构。你可以把它当作学习地图。
好了,这一章的内容就到这里。NumPy是数据分析的基石,我建议你多动手敲代码。我在做储能项目时,每天都要和数组打交道,熟练了之后效率会高很多。