NumPy 从基础到进阶:数据科学家必读
在数据科学的世界里,高效的数据处理和数值计算是基石。NumPy (Numerical Python) 正是 Python 生态系统中解决这一核心需求的强大库。它提供了高性能的多维数组对象,以及用于处理这些数组的工具。对于任何数据科学家来说,熟练掌握 NumPy 不仅仅是锦上添花,更是不可或缺的生存技能。
本文将带领您从 NumPy 的基础概念出发,逐步深入到其高级功能,揭示它如何在数据科学工作流中发挥关键作用。
1. NumPy 基础:数组与核心概念
1.1 为什么是 NumPy?
Python 原生的列表 (list) 可以存储不同类型的数据,但在进行大规模数值计算时效率低下。NumPy 的 ndarray 对象则专门设计用于存储同质化的数值数据,并利用 C/Fortran 等底层语言进行优化,从而实现了显著的性能提升。此外,NumPy 提供了大量针对数组操作的优化函数,避免了编写复杂的循环。
1.2 ndarray:NumPy 的核心
ndarray (n-dimensional array) 是 NumPy 的核心数据结构,它是一个存储相同类型元素的网格。
– 维度 (Dimensions/Axes):数组的轴数。例如,一个一维数组有一个轴,一个矩阵有两个轴。
– 形状 (Shape):一个元组,表示数组在每个维度上的大小。例如,一个 2×3 的矩阵形状为 (2, 3)。
– 数据类型 (Data Type / dtype):数组中元素的类型,如 int32, float64 等。所有元素必须是同一类型。
创建数组:
“`python
import numpy as np
从 Python 列表创建
arr1 = np.array([1, 2, 3, 4, 5])
print(f”arr1: {arr1}, shape: {arr1.shape}, dtype: {arr1.dtype}”)
多维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(f”arr2:\n{arr2}, shape: {arr2.shape}”)
使用内置函数创建
zeros_arr = np.zeros((2, 3)) # 全零数组
ones_arr = np.ones((3, 2), dtype=int) # 全一数组,指定整数类型
arange_arr = np.arange(0, 10, 2) # 类似 range()
linspace_arr = np.linspace(0, 1, 5) # 0到1之间等距的5个点
identity_matrix = np.eye(3) # 单位矩阵
“`
1.3 基本数组操作
NumPy 数组支持直观的元素级数学运算。
“`python
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(f”a + b: {a + b}”) # 数组相加
print(f”a * 2: {a * 2}”) # 标量乘法
print(f”a ** 2: {a ** 2}”) # 元素级平方
矩阵乘法 (@ 或 np.dot)
matrix_a = np.array([[1, 2], [3, 4]])
matrix_b = np.array([[5, 6], [7, 8]])
print(f”Matrix multiplication:\n{matrix_a @ matrix_b}”)
“`
1.4 广播 (Broadcasting)
广播是 NumPy 的一个强大特性,它允许不同形状的数组在特定条件下进行算术运算,而无需显式地复制数据。简单来说,当两个数组的维度不匹配时,NumPy 会尝试“扩展”较小数组的维度,使其与较大数组兼容。
广播规则:
1. 如果两个数组的维度数不同,那么维度较小的数组的形状会在其前面用 1 填充。
2. 对于任一维度,如果两个数组在该维度上的大小相同,或者其中一个数组在该维度上的大小为 1,那么它们是兼容的。
3. 如果两个数组在任何维度上的大小都不同且都不是 1,则会引发错误。
“`python
arr = np.array([[1, 2, 3], [4, 5, 6]]) # 形状 (2, 3)
scalar = 10 # 形状 ()
print(f”arr + scalar:\n{arr + scalar}”) # 标量被广播到整个数组
vec = np.array([10, 20, 30]) # 形状 (3,)
print(f”arr + vec:\n{arr + vec}”) # vec 被广播到 arr 的每一行
“`
2. NumPy 进阶:索引、切片与高级操作
2.1 索引与切片
类似于 Python 列表,NumPy 数组也支持强大的索引和切片功能,但更灵活且功能更强大。
“`python
data = np.arange(1, 17).reshape(4, 4)
print(f”Original data:\n{data}”)
基本索引
print(f”Element at (0, 0): {data[0, 0]}”)
print(f”First row: {data[0, :]}”) # 或 data[0]
print(f”First column: {data[:, 0]}”)
切片
print(f”Sub-array (rows 0-1, cols 1-2):\n{data[0:2, 1:3]}”)
print(f”Last row: {data[-1, :]}”)
print(f”Every other row:\n{data[::2, :]}”)
布尔索引:根据条件选择元素
mask = data > 8
print(f”Elements > 8:\n{data[mask]}”)
print(f”Elements > 8 (alternative):\n{data[data > 8]}”)
花式索引 (Fancy Indexing):使用整数数组作为索引
rows = np.array([0, 2])
cols = np.array([1, 3])
print(f”Elements at (0,1) and (2,3):\n{data[rows, cols]}”) # 返回 [2 12]
使用一个整数数组选择行
print(f”Selected rows (0, 2):\n{data[[0, 2]]}”)
“`
2.2 形状操作 (Reshaping, Stacking)
改变数组的形状是数据预处理的常见操作。
“`python
arr = np.arange(1, 10) # [1 2 3 4 5 6 7 8 9]
reshape: 不改变数据,只改变数组的视图
reshaped_arr = arr.reshape(3, 3)
print(f”Reshaped:\n{reshaped_arr}”)
flatten: 将多维数组展平为一维数组
flat_arr = reshaped_arr.flatten()
print(f”Flattened: {flat_arr}”)
transpose: 转置数组
print(f”Transposed:\n{reshaped_arr.T}”)
concatenate: 沿指定轴连接数组
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
垂直堆叠 (行增加)
vstack_arr = np.vstack((a, b))
print(f”VStack:\n{vstack_arr}”)
水平堆叠 (列增加)
hstack_arr = np.hstack((a, b))
print(f”HStack:\n{hstack_arr}”)
dstack: 沿第三个轴堆叠 (通常用于图像处理,堆叠通道)
column_stack: 将一维数组作为列堆叠成二维数组
row_stack: 等同于 vstack
“`
2.3 通用函数 (Universal Functions – ufuncs)
ufuncs 是对 ndarray 中的每个元素执行操作的函数。它们是矢量化的,这意味着它们直接在整个数组上操作,比 Python 的循环快得多。常见的 ufuncs 包括数学运算(np.add, np.sqrt, np.exp, np.sin 等)、比较运算(np.greater, np.equal 等)和聚合函数。
“`python
x = np.arange(1, 6) # [1 2 3 4 5]
print(f”Square root: {np.sqrt(x)}”)
print(f”Exponential: {np.exp(x)}”)
print(f”Max element: {np.max(x)}”)
print(f”Sum of elements: {np.sum(x)}”)
print(f”Mean of elements: {np.mean(x)}”)
print(f”Standard deviation: {np.std(x)}”)
matrix = np.array([[1, 2, 3], [4, 5, 6]])
print(f”Sum along axis 0 (columns): {np.sum(matrix, axis=0)}”) # [5 7 9]
print(f”Sum along axis 1 (rows): {np.sum(matrix, axis=1)}”) # [ 6 15]
“`
2.4 线性代数
NumPy 的 linalg 模块提供了强大的线性代数功能,对于机器学习和统计建模至关重要。
“`python
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
矩阵乘法
print(f”Matrix product (dot):\n{np.dot(A, B)}”)
print(f”Matrix product (@ operator):\n{A @ B}”)
逆矩阵
inv_A = np.linalg.inv(A)
print(f”Inverse of A:\n{inv_A}”)
特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(A)
print(f”Eigenvalues: {eigenvalues}”)
print(f”Eigenvectors:\n{eigenvectors}”)
求解线性方程组 Ax = b
b_vec = np.array([9, 10])
x_solution = np.linalg.solve(A, b_vec)
print(f”Solution to Ax=b: {x_solution}”)
“`
3. NumPy 在数据科学中的高级应用
3.1 性能优化与内存管理
NumPy 数组的同质性是其高性能的关键。数据连续存储在内存中,可以利用 CPU 的 SIMD (单指令多数据) 指令进行批量处理。
- 矢量化操作:尽量避免 Python 循环,转而使用 NumPy 的 ufuncs 和数组操作。
- 就地操作:一些操作(如
+=,*=)是就地 (in-place) 操作,可以减少内存分配和复制。 - 视图 vs 副本:切片通常返回原始数组的视图 (view),而不是副本 (copy),这意味着对视图的修改会影响原始数组。使用
.copy()明确创建副本。
“`python
large_arr = np.random.rand(1000000)
避免循环 (慢)
result = [x * 2 for x in large_arr]
使用矢量化操作 (快)
result = large_arr * 2
“`
3.2 随机数生成
np.random 模块提供了各种概率分布的随机数生成功能,在模拟、采样和模型初始化中非常有用。
“`python
均匀分布
uniform_samples = np.random.rand(3, 3) # [0, 1) 之间的随机数
正态分布 (均值0,标准差1)
normal_samples = np.random.randn(3, 3)
指定范围内的整数
random_integers = np.random.randint(0, 10, size=(2, 4)) # [0, 10) 之间
从给定序列中随机选择
choices = np.random.choice([‘apple’, ‘banana’, ‘cherry’], size=5, replace=True)
“`
3.3 与其他库的集成
NumPy 是 Python 数据科学生态系统的基石,与 Pandas、Matplotlib、Scikit-learn 等库无缝集成。
- Pandas:Pandas 的 DataFrame 和 Series 对象内部基于 NumPy 数组构建。
python
import pandas as pd
df = pd.DataFrame(np.random.randint(0, 100, size=(5, 3)), columns=['A', 'B', 'C'])
print(df) - Matplotlib:绘图库通常接受 NumPy 数组作为输入。
python
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)
plt.plot(x, y)
plt.title("Sine Wave")
plt.show() - Scikit-learn:大多数机器学习模型的输入数据都是 NumPy 数组。
3.4 结构化数组与记录数组 (Structured Arrays / Record Arrays)
当您需要存储混合数据类型(例如,名称、年龄、体重)但仍希望利用 NumPy 的高性能时,结构化数组非常有用。
python
data_type = [('name', 'S10'), ('age', 'i4'), ('weight', 'f8')]
people = np.array([("Alice", 25, 65.5), ("Bob", 30, 78.2)], dtype=data_type)
print(people)
print(f"Names: {people['name']}")
print(f"Ages: {people['age']}")
总结
NumPy 不仅仅是一个数值计算库,它更是数据科学家处理、分析和理解数据的强大工具。从基础的数组创建和操作,到高级的广播、索引和线性代数功能,NumPy 为高效的数据处理提供了坚实的基础。
熟练掌握 NumPy,您将能够:
– 以更快的速度处理大规模数据集。
– 编写更简洁、更可读的代码。
– 更好地理解和利用数据科学中各种算法的数学原理。
– 无缝地与 Python 数据科学生态系统中的其他工具协作。
继续实践,探索 NumPy 的每一个角落,它将成为您数据科学旅程中最忠实的伙伴。