Day 1: NumPy基础
学习目标
- 理解NumPy库的核心概念和重要性
- 掌握NumPy数组的创建、操作和计算方法
- 学习NumPy的广播机制和向量化计算
- 了解NumPy在科学计算和机器学习中的应用
- 对比JAVA和NumPy在数值计算方面的差异
1. NumPy简介
1.1 什么是NumPy
NumPy(Numerical Python)是Python科学计算的基础库,提供了高性能的多维数组对象和用于处理这些数组的工具。
定义:NumPy是一个开源的Python库,用于科学计算,提供了强大的N维数组对象、复杂的广播功能、工具函数、线性代数、傅里叶变换和随机数生成等功能。
核心特点:
- 高性能的多维数组对象(ndarray)
- 用于对数组执行数学运算的函数
- 线性代数、傅里叶变换和随机数生成工具
- C/C++和Fortran代码的集成工具
1.2 为什么需要NumPy
Python列表的局限性:
- 存储效率低(每个元素都是一个Python对象)
- 计算效率低(需要类型检查和函数调用)
- 缺乏科学计算所需的专用函数
NumPy的优势:
- 内存效率高(连续内存块,类型一致)
- 计算效率高(向量化操作,底层C实现)
- 提供丰富的数学函数和操作
- 与其他科学计算库无缝集成
1.3 NumPy与JAVA对比
| 特性 | JAVA | NumPy |
|---|---|---|
| 数组类型 | 固定类型数组 | 多维同构数组 |
| 内存管理 | 自动垃圾回收 | 连续内存块 |
| 数组操作 | 需要显式循环 | 向量化操作 |
| 性能优化 | JIT编译 | C/Fortran实现 |
| 科学计算 | 需要第三方库(如Apache Commons Math) | 内置丰富功能 |
JAVA示例:
// JAVA中的数组操作
double[] a = {1, 2, 3, 4};
double[] b = {5, 6, 7, 8};
double[] result = new double[a.length];
// 元素级别的加法
for (int i = 0; i < a.length; i++) {
result[i] = a[i] + b[i];
}
// 计算平均值
double sum = 0;
for (double value : result) {
sum += value;
}
double mean = sum / result.length;
NumPy示例:
# NumPy中的数组操作
import numpy as np
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
# 元素级别的加法(向量化)
result = a + b
# 计算平均值
mean = result.mean()
2. NumPy数组基础
2.1 创建NumPy数组
从Python列表创建:
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]]
# 指定数据类型
arr3 = np.array([1, 2, 3], dtype=float)
print(arr3) # [1. 2. 3.]
使用NumPy函数创建:
# 创建全0数组
zeros = np.zeros((3, 4)) # 3行4列的全0数组
# 创建全1数组
ones = np.ones((2, 3, 4)) # 2x3x4的全1数组
# 创建指定值数组
full = np.full((2, 2), 7) # 2x2的全7数组
# 创建单位矩阵
eye = np.eye(3) # 3x3单位矩阵
# 创建等间隔数组
linear = np.linspace(0, 10, 5) # 0到10之间的5个等间隔数字
arange = np.arange(0, 10, 2) # 0到10之间步长为2的数组
# 创建随机数组
random = np.random.random((2, 3)) # 2x3的随机数组(0到1之间)
normal = np.random.normal(0, 1, (2, 3)) # 均值0,标准差1的正态分布随机数
2.2 数组属性和信息
arr = np.array([[1, 2, 3], [4, 5, 6]])
# 形状
print(arr.shape) # (2, 3)
# 维度
print(arr.ndim) # 2
# 元素总数
print(arr.size) # 6
# 数据类型
print(arr.dtype) # int64
# 每个元素的字节大小
print(arr.itemsize) # 8
# 数组的总字节大小
print(arr.nbytes) # 48
2.3 数组索引和切片
一维数组索引:
arr = np.array([1, 2, 3, 4, 5])
# 单个元素
print(arr[0]) # 1
print(arr[-1]) # 5
# 切片
print(arr[1:4]) # [2 3 4]
print(arr[:3]) # [1 2 3]
print(arr[2:]) # [3 4 5]
print(arr[::2]) # [1 3 5] (步长为2)
多维数组索引:
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 单个元素
print(arr[0, 0]) # 1
print(arr[2, 2]) # 9
# 行索引
print(arr[1]) # [4 5 6]
# 切片
print(arr[0:2, 1:3])
# [[2 3]
# [5 6]]
# 混合索引和切片
print(arr[0, :]) # [1 2 3]
print(arr[:, 1]) # [2 5 8]
# 布尔索引
mask = arr > 5
print(mask)
# [[False False False]
# [False False True]
# [ True True True]]
print(arr[mask]) # [6 7 8 9]
高级索引:
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 整数索引数组
rows = np.array([0, 2])
cols = np.array([0, 2])
print(arr[rows, cols]) # [1 9]
# 使用ix_函数获取网格点
print(arr[np.ix_([0, 2], [0, 2])])
# [[1 3]
# [7 9]]
2.4 数组形状操作
arr = np.array([[1, 2, 3], [4, 5, 6]])
# 重塑数组
reshaped = arr.reshape(3, 2)
print(reshaped)
# [[1 2]
# [3 4]
# [5 6]]
# 展平数组
flattened = arr.flatten()
print(flattened) # [1 2 3 4 5 6]
# 转置数组
transposed = arr.T
print(transposed)
# [[1 4]
# [2 5]
# [3 6]]
# 调整数组大小(可能会复制数据)
resized = np.resize(arr, (3, 3))
print(resized)
# [[1 2 3]
# [4 5 6]
# [1 2 3]]
3. NumPy数组操作
3.1 数组运算
算术运算:
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 加法
print(a + b) # [5 7 9]
print(np.add(a, b)) # [5 7 9]
# 减法
print(a - b) # [-3 -3 -3]
print(np.subtract(a, b)) # [-3 -3 -3]
# 乘法
print(a * b) # [4 10 18]
print(np.multiply(a, b)) # [4 10 18]
# 除法
print(a / b) # [0.25 0.4 0.5]
print(np.divide(a, b)) # [0.25 0.4 0.5]
# 幂运算
print(a ** 2) # [1 4 9]
print(np.power(a, 2)) # [1 4 9]
# 取模
print(a % 2) # [1 0 1]
print(np.mod(a, 2)) # [1 0 1]
统计运算:
arr = np.array([[1, 2, 3], [4, 5, 6]])
# 求和
print(np.sum(arr)) # 21
print(arr.sum()) # 21
# 按轴求和
print(np.sum(arr, axis=0)) # [5 7 9] (按列求和)
print(np.sum(arr, axis=1)) # [6 15] (按行求和)
# 最小值/最大值
print(np.min(arr)) # 1
print(np.max(arr)) # 6
# 平均值
print(np.mean(arr)) # 3.5
# 标准差
print(np.std(arr)) # 1.707825127659933
# 方差
print(np.var(arr)) # 2.9166666666666665
# 累积和
print(np.cumsum(arr)) # [1 3 6 10 15 21]
比较运算:
a = np.array([1, 2, 3])
b = np.array([3, 2, 1])
# 元素级比较
print(a == b) # [False True False]
print(a > b) # [False False True]
print(a < b) # [ True False False]
# 数组比较
print(np.array_equal(a, b)) # False
# 逻辑运算
print(np.logical_and(a > 1, b < 3)) # [False True False]
print(np.logical_or(a > 2, b > 2)) # [ True False True]
print(np.logical_not(a > 2)) # [ True True False]
3.2 广播机制
广播(Broadcasting)是NumPy的一个强大特性,允许在算术运算期间处理不同形状的数组。
广播规则:
- 如果两个数组的维度数不同,形状较小的数组会在前面补1
- 如果两个数组的形状在任何维度上不匹配,但其中一个维度为1,则该维度会被扩展以匹配另一个数组的形状
- 如果两个数组的形状在任何维度上不匹配,且没有一个维度为1,则会引发错误
# 标量与数组运算
arr = np.array([1, 2, 3, 4])
print(arr + 10) # [11 12 13 14]
# 不同形状数组的运算
a = np.array([[1, 2, 3], [4, 5, 6]]) # 形状(2, 3)
b = np.array([10, 20, 30]) # 形状(3,)
print(a + b)
# [[11 22 33]
# [14 25 36]]
# 广播过程可视化
# b被广播为形状(2, 3)的数组:
# [[10, 20, 30],
# [10, 20, 30]]
# 更复杂的广播示例
c = np.array([[1], [2]]) # 形状(2, 1)
print(a + c)
# [[ 2 3 4]
# [ 6 7 8]]
# c被广播为形状(2, 3)的数组:
# [[1, 1, 1],
# [2, 2, 2]]
3.3 向量化操作
向量化是NumPy的核心优势之一,它允许对整个数组执行操作,而不需要显式循环,从而提高性能。
向量化vs循环:
import time
# 创建大数组
size = 10000000
a = np.random.random(size)
b = np.random.random(size)
# 使用循环
start = time.time()
result = np.zeros(size)
for i in range(size):
result[i] = a[i] + b[i]
print(f"循环耗时: {time.time() - start} 秒")
# 使用向量化操作
start = time.time()
result = a + b
print(f"向量化耗时: {time.time() - start} 秒")
向量化函数:
# 数学函数
x = np.array([1, 2, 3, 4])
print(np.sqrt(x)) # [1. 1.41421356 1.73205081 2.]
print(np.exp(x)) # [ 2.71828183 7.3890561 20.08553692 54.59815003]
print(np.log(x)) # [0. 0.69314718 1.09861229 1.38629436]
print(np.sin(x)) # [0.84147098 0.90929743 0.14112001 -0.7568025]
# 条件函数
print(np.where(x > 2, x, -x)) # [-1 -2 3 4]
4. NumPy高级功能
4.1 线性代数操作
NumPy提供了丰富的线性代数功能,这些功能在机器学习和深度学习中非常重要。
# 矩阵乘法
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
print(np.dot(a, b))
# [[19 22]
# [43 50]]
print(a @ b) # Python 3.5+的矩阵乘法运算符
# [[19 22]
# [43 50]]
# 矩阵特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(a)
print("特征值:", eigenvalues)
print("特征向量:", eigenvectors)
# 矩阵求逆
inv_a = np.linalg.inv(a)
print(inv_a)
# [[-2. 1. ]
# [ 1.5 -0.5]]
# 解线性方程组 Ax = b
b = np.array([1, 2])
x = np.linalg.solve(a, b)
print(x) # [-1. 1.]
# 矩阵分解
u, s, vh = np.linalg.svd(a)
print("U:", u)
print("S:", s)
print("V^H:", vh)
4.2 随机数生成
NumPy提供了强大的随机数生成功能,这在模拟、采样和初始化机器学习模型时非常有用。
# 设置随机种子(为了结果可重现)
np.random.seed(42)
# 均匀分布
uniform = np.random.uniform(0, 1, size=(2, 3))
print(uniform)
# 正态分布
normal = np.random.normal(0, 1, size=(2, 3))
print(normal)
# 整数随机数
integers = np.random.randint(0, 10, size=(2, 3))
print(integers)
# 随机排列
arr = np.arange(10)
np.random.shuffle(arr)
print(arr)
# 随机选择
choices = np.random.choice(arr, size=5, replace=False)
print(choices)
# 多项分布采样
multinomial = np.random.multinomial(10, [0.2, 0.3, 0.5], size=5)
print(multinomial)
4.3 文件操作
NumPy提供了保存和加载数组的功能,支持多种文件格式。
arr = np.array([[1, 2, 3], [4, 5, 6]])
# 保存为二进制文件
np.save('array.npy', arr)
# 加载二进制文件
loaded_arr = np.load('array.npy')
print(loaded_arr)
# 保存为文本文件
np.savetxt('array.txt', arr, delimiter=',')
# 加载文本文件
loaded_txt = np.loadtxt('array.txt', delimiter=',')
print(loaded_txt)
# 保存多个数组
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
np.savez('arrays.npz', a=a, b=b)
# 加载多个数组
data = np.load('arrays.npz')
print(data['a'])
print(data['b'])
5. NumPy在机器学习中的应用
5.1 数据预处理
# 标准化
data = np.array([[1, 2], [3, 4], [5, 6]])
mean = np.mean(data, axis=0)
std = np.std(data, axis=0)
normalized = (data - mean) / std
print(normalized)
# 归一化
min_vals = np.min(data, axis=0)
max_vals = np.max(data, axis=0)
normalized = (data - min_vals) / (max_vals - min_vals)
print(normalized)
# 缺失值处理
data = np.array([1, 2, np.nan, 4, 5])
print(np.isnan(data)) # [False False True False False]
print(np.nanmean(data)) # 3.0
print(np.nansum(data)) # 12.0
5.2 特征工程
# 多项式特征
x = np.array([1, 2, 3, 4])
poly_features = np.column_stack([x, x**2, x**3])
print(poly_features)
# [[ 1 1 1]
# [ 2 4 8]
# [ 3 9 27]
# [ 4 16 64]]
# 独热编码
categories = np.array([0, 1, 2, 1, 0])
one_hot = np.eye(3)[categories]
print(one_hot)
# [[1. 0. 0.]
# [0. 1. 0.]
# [0. 0. 1.]
# [0. 1. 0.]
# [1. 0. 0.]]
5.3 模型实现示例
使用NumPy实现简单的线性回归:
# 生成示例数据
np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
# 添加偏置项
X_b = np.c_[np.ones((100, 1)), X]
# 使用正规方程求解
theta = np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y
print(theta) # 大约 [4, 3]
# 预测
X_new = np.array([[0], [2]])
X_new_b = np.c_[np.ones((2, 1)), X_new]
y_predict = X_new_b @ theta
print(y_predict)
6. 从JAVA开发者视角理解NumPy
6.1 概念对应关系
| JAVA概念 | NumPy概念 |
|---|---|
| 数组(Array) | ndarray |
| 多维数组 | 多维ndarray |
| for循环操作 | 向量化操作 |
| Apache Commons Math | NumPy内置函数 |
| 类型转换 | astype方法 |
| System.arraycopy | 切片和视图 |
6.2 性能考量
JAVA数组操作:
- 类型安全但操作冗长
- 需要显式循环,代码量大
- JIT编译可提供良好性能
- 并行处理需要显式编程
NumPy数组操作:
- 语法简洁,向量化操作
- 底层C实现,性能优化
- 适合数据分析和科学计算
- 受GIL限制,单线程执行
6.3 实现对比
JAVA实现矩阵乘法:
public static double[][] matrixMultiply(double[][] a, double[][] b) {
int rowsA = a.length;
int colsA = a[0].length;
int colsB = b[0].length;
double[][] result = new double[rowsA][colsB];
for (int i = 0; i < rowsA; i++) {
for (int j = 0; j < colsB; j++) {
for (int k = 0; k < colsA; k++) {
result[i][j] += a[i][k] * b[k][j];
}
}
}
return result;
}
NumPy实现矩阵乘法:
def matrix_multiply(a, b):
return np.dot(a, b)
7. 实践练习
练习1:基本操作
- 创建一个形状为(3, 4)的随机整数数组,范围在0到10之间
- 计算数组的均值、标准差、最小值和最大值
- 将数组中所有大于5的元素替换为5(剪裁操作)
练习2:图像处理
- 创建一个8x8的棋盘图案数组(0和1交替)
- 使用NumPy的广播机制,将棋盘图案扩展为一个64x64的大棋盘
- 将棋盘图案旋转90度
练习3:数据分析
- 生成100个服从正态分布的随机数(均值为0,标准差为1)
- 计算这些数据的百分位数(25%,50%,75%)
- 将数据标准化(减去均值,除以标准差)
- 计算标准化后数据的协方差矩阵
8. 总结与反思
- NumPy是Python科学计算的基础库,提供了高性能的多维数组对象和丰富的函数
- NumPy的核心优势在于向量化操作和广播机制,可以大幅提高计算效率
- 相比JAVA的数组操作,NumPy提供了更简洁的语法和更丰富的功能
- NumPy在数据预处理、特征工程和模型实现等机器学习任务中发挥着重要作用
- JAVA开发者可以利用已有的数组和矩阵概念快速理解NumPy,但需要适应向量化思维
9. 预习与延伸阅读
预习内容
- Pandas库的基本概念和数据结构(Series和DataFrame)
- 数据读写和数据清洗方法
- 数据分析和转换操作
延伸阅读
- Travis E. Oliphant,《A Guide to NumPy》
- Wes McKinney,《Python for Data Analysis》(第2章和第4章)
- Jake VanderPlas,《Python Data Science Handbook》(第2章)
- NumPy官方文档:numpy.org/doc/
10. 明日预告
明天我们将学习Pandas库,这是基于NumPy构建的数据分析工具,提供了更高级的数据结构和数据操作功能。我们将探讨Series和DataFrame对象、数据读写方法、数据清洗技术以及数据分析和转换操作。