第二阶段_技术栈过渡-Day 1: NumPy基础

38 阅读11分钟

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对比

特性JAVANumPy
数组类型固定类型数组多维同构数组
内存管理自动垃圾回收连续内存块
数组操作需要显式循环向量化操作
性能优化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
  2. 如果两个数组的形状在任何维度上不匹配,但其中一个维度为1,则该维度会被扩展以匹配另一个数组的形状
  3. 如果两个数组的形状在任何维度上不匹配,且没有一个维度为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 MathNumPy内置函数
类型转换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:基本操作

  1. 创建一个形状为(3, 4)的随机整数数组,范围在0到10之间
  2. 计算数组的均值、标准差、最小值和最大值
  3. 将数组中所有大于5的元素替换为5(剪裁操作)

练习2:图像处理

  1. 创建一个8x8的棋盘图案数组(0和1交替)
  2. 使用NumPy的广播机制,将棋盘图案扩展为一个64x64的大棋盘
  3. 将棋盘图案旋转90度

练习3:数据分析

  1. 生成100个服从正态分布的随机数(均值为0,标准差为1)
  2. 计算这些数据的百分位数(25%,50%,75%)
  3. 将数据标准化(减去均值,除以标准差)
  4. 计算标准化后数据的协方差矩阵

8. 总结与反思

  • NumPy是Python科学计算的基础库,提供了高性能的多维数组对象和丰富的函数
  • NumPy的核心优势在于向量化操作和广播机制,可以大幅提高计算效率
  • 相比JAVA的数组操作,NumPy提供了更简洁的语法和更丰富的功能
  • NumPy在数据预处理、特征工程和模型实现等机器学习任务中发挥着重要作用
  • JAVA开发者可以利用已有的数组和矩阵概念快速理解NumPy,但需要适应向量化思维

9. 预习与延伸阅读

预习内容

  • Pandas库的基本概念和数据结构(Series和DataFrame)
  • 数据读写和数据清洗方法
  • 数据分析和转换操作

延伸阅读

  1. Travis E. Oliphant,《A Guide to NumPy》
  2. Wes McKinney,《Python for Data Analysis》(第2章和第4章)
  3. Jake VanderPlas,《Python Data Science Handbook》(第2章)
  4. NumPy官方文档:numpy.org/doc/

10. 明日预告

明天我们将学习Pandas库,这是基于NumPy构建的数据分析工具,提供了更高级的数据结构和数据操作功能。我们将探讨Series和DataFrame对象、数据读写方法、数据清洗技术以及数据分析和转换操作。