NumPy
NumPy (Numerical Python) 是 Python 科学计算的基础包,提供了强大的多维数组对象和用于处理这些数组的工具。它是 Pandas、SciPy、Matplotlib 等库的基础。
简介
NumPy 特性
NumPy 核心特性:
- ndarray: 强大的N维数组对象
- 广播功能: 用于对数组进行形状不匹配的运算
- 集成 C/C++/Fortran 代码的工具
- 线性代数、傅里叶变换、随机数生成功能
- 用于对整组数据进行快速运算的标准数学函数
- 比 Python 列表快 10-100 倍
- 内存占用更小
适用场景:
- 科学计算和数值分析
- 数据分析和处理
- 机器学习和深度学习
- 图像和信号处理
- 矩阵运算和线性代数
- 统计分析
安装 NumPy
# 创建虚拟环境
python -m venv venv
# Windows 激活
venv\Scripts\activate
# Linux/Mac 激活
source venv/bin/activate
# 安装 NumPy
pip install numpy
# 安装特定版本
pip install numpy==1.24.0
# 使用 conda 安装
conda install numpy
# 查看版本
python -c "import numpy; print(numpy.__version__)"
快速开始
导入和配置
import numpy as np
# 查看版本
print(np.__version__)
# 查看配置
print(np.show_config())
# 设置打印选项
np.set_printoptions(precision=4, suppress=True) # 精度和抑制科学计数法
np.set_printoptions(threshold=10) # 打印阈值
# 查看打印选项
print(np.get_printoptions())
ndarray 对象
创建数组
import numpy as np
# 从列表创建一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1)
# 从列表创建二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2)
# 指定数据类型
arr3 = np.array([1, 2, 3], dtype='float64')
arr4 = np.array([1, 2, 3], dtype=np.int32)
# 从元组创建
arr5 = np.array((1, 2, 3))
# 创建复数数组
arr6 = np.array([1+2j, 3+4j, 5+6j])
数组属性
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) # int32 或 int64
# 每个元素大小(字节)
print(arr.itemsize) # 4 或 8
# 数据指针
print(arr.data) # 内存地址
# 转置
print(arr.T) # 转置矩阵
# 扁平化
print(arr.flat) # 扁平迭代器
数据类型
# 常用数据类型
int8 = np.int8 # 8位整数 (-128 到 127)
int16 = np.int16 # 16位整数
int32 = np.int32 # 32位整数
int64 = np.int64 # 64位整数
uint8 = np.uint8 # 无符号8位整数 (0 到 255)
uint16 = np.uint16
uint32 = np.uint32
uint64 = np.uint64
float16 = np.float16 # 16位浮点数
float32 = np.float32 # 32位浮点数
float64 = np.float64 # 64位浮点数
complex64 = np.complex64 # 64位复数
complex128 = np.complex128 # 128位复数
bool_ = np.bool_ # 布尔类型
object_ = np.object_ # Python 对象类型
string_ = np.string_ # 字符串类型
unicode_ = np.unicode_ # Unicode 类型
# 类型转换
arr = np.array([1.5, 2.5, 3.5])
arr_int = arr.astype(int) # 转整数
arr_float = arr.astype(float32) # 转32位浮点
arr_str = arr.astype(str) # 转字符串
# 查看类型
print(arr.dtype) # 查看数据类型
print(arr.dtype.name) # 类型名称
print(arr.dtype.kind) # 类型种类(i,u,f,c,b,M,O,S,U)
数组创建
从列表创建
# 一维数组
arr1 = np.array([1, 2, 3, 4, 5])
# 二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
# 三维数组
arr3 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
# 从元组创建
arr4 = np.array((1, 2, 3, 4, 5))
arange
# 类似 Python 的 range
arr = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
arr = np.arange(1, 10) # [1 2 3 4 5 6 7 8 9]
arr = np.arange(1, 10, 2) # [1 3 5 7 9]
arr = np.arange(0, 1, 0.1) # [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]
# 指定类型
arr = np.arange(10, dtype=float)
linspace
# 线性间隔
arr = np.linspace(0, 10, 5) # [ 0. 2.5 5. 7.5 10. ]
arr = np.linspace(0, 10, 5, endpoint=False) # [0. 2. 4. 6. 8.]
arr = np.linspace(0, 10, 5, retstep=True) # 返回数组和步长
# logspace: 对数间隔
arr = np.logspace(0, 2, 5) # [ 1. 1.77827941 3.16227766 5.62341325 10. ]
zeros/ones
# zeros: 全零数组
arr = np.zeros(5) # 一维全零数组
arr = np.zeros((2, 3)) # 二维全零数组
arr = np.zeros((2, 3, 4)) # 三维全零数组
arr = np.zeros_like(arr) # 按数组形状创建全零数组
# ones: 全一数组
arr = np.ones(5) # 一维全一数组
arr = np.ones((2, 3)) # 二维全一数组
arr = np.ones_like(arr) # 按数组形状创建全一数组
# empty: 未初始化数组
arr = np.empty(5) # 未初始化,内容随机
arr = np.empty((2, 3))
arr = np.empty_like(arr)
# full: 填充指定值
arr = np.full((2, 3), 5) # 填充5
arr = np.full_like(arr, 10) # 按形状填充10
eye/identity
# eye: 单位矩阵
arr = np.eye(3) # 3x3 单位矩阵
arr = np.eye(3, k=1) # 对角线偏移
arr = np.eye(3, 4) # 3x4 矩阵
# identity: 方阵单位矩阵
arr = np.identity(3)
# diag: 对角矩阵
arr = np.diag([1, 2, 3]) # 对角矩阵
arr = np.diag([[1, 2, 3], [4, 5, 6]]) # 提取对角线
random
# 随机数生成
arr = np.random.rand(5) # [0,1) 均匀分布
arr = np.random.rand(2, 3) # 2x3 均匀分布
arr = np.random.randn(5) # 标准正态分布
arr = np.random.randn(2, 3) # 2x3 正态分布
arr = np.random.randint(0, 10, 5) # 整数随机 [0,10)
arr = np.random.randint(0, 10, size=(2, 3)) # 2x3 整数随机
# 选择随机元素
arr = np.random.choice([1, 2, 3, 4, 5], size=3)
arr = np.random.choice(10, size=5, replace=False) # 不重复
# 随机排列
arr = np.random.permutation(10) # 随机排列
np.random.shuffle(arr) # 原地打乱
# 设置种子
np.random.seed(42)
# 其他分布
arr = np.random.uniform(0, 10, 5) # 均匀分布
arr = np.random.normal(0, 1, 5) # 正态分布(均值,标准差)
arr = np.random.beta(2, 5, 5) # Beta分布
arr = np.random.gamma(2, 2, 5) # Gamma分布
数组索引
基本索引
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 一维数组索引
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1[0]) # 第一个元素
print(arr1[-1]) # 最后一个元素
# 二维数组索引
print(arr[0, 0]) # 第1行第1列
print(arr[0][0]) # 同上
print(arr[1, 2]) # 第2行第3列
print(arr[-1, -1]) # 最后一个元素
# 获取行
print(arr[0]) # 第1行
print(arr[1]) # 第2行
# 获取列
print(arr[:, 0]) # 第1列
print(arr[:, 1]) # 第2列
切片
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
# 一维切片
arr1 = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(arr1[2:7]) # [2 3 4 5 6]
print(arr1[:5]) # [0 1 2 3 4]
print(arr1[5:]) # [5 6 7 8 9]
print(arr1[::2]) # [0 2 4 6 8] 步长为2
print(arr1[::-1]) # [9 8 7 6 5 4 3 2 1 0] 反转
# 二维切片
print(arr[0:2, :]) # 前2行所有列
print(arr[:, 1:3]) # 所有行第2-3列
print(arr[0:2, 1:3]) # 前2行,第2-3列
print(arr[::2, ::2]) # 步长为2
print(arr[::-1, ::-1]) # 行列都反转
# 省略号
arr3 = np.arange(27).reshape(3, 3, 3)
print(arr3[..., 0]) # 所有维度的第1列
整数数组索引
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 使用整数数组索引
rows = np.array([0, 1, 2])
cols = np.array([0, 1, 2])
print(arr[rows, cols]) # [1 5 9] 对角线元素
# 一维数组索引
arr1 = np.array([10, 20, 30, 40, 50])
indices = np.array([0, 2, 4])
print(arr1[indices]) # [10 30 50]
# 二维数组索引
print(arr[[0, 1], [0, 1]]) # [1 5]
print(arr[[0, 1, 2], [0, 1, 2]]) # [1 5 9]
# 结合切片
print(arr[1:, [0, 2]]) # 后2行的第1和第3列
布尔索引
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# 布尔条件
mask = arr > 5
print(arr[mask]) # [ 6 7 8 9 10]
# 多条件
print(arr[(arr > 3) & (arr < 8)]) # [4 5 6 7] 与
print(arr[(arr < 3) | (arr > 8)]) # [1 2 9 10] 或
print(arr[~(arr > 5)]) # [1 2 3 4 5] 非
# 二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr2[arr2 > 5]) # [6 7 8 9]
# 赋值
arr[arr > 5] = 0 # 大于5的设为0
arr[(arr > 3) & (arr < 7)] = 99 # 3-7之间设为99
# where 函数
result = np.where(arr > 5, arr, 0) # 大于5保留,否则为0
数组运算
算术运算
arr1 = np.array([1, 2, 3, 4])
arr2 = np.array([5, 6, 7, 8])
# 基本运算
print(arr1 + arr2) # [ 6 8 10 12] 加法
print(arr1 - arr2) # [-4 -4 -4 -4] 减法
print(arr1 * arr2) # [ 5 12 21 32] 乘法
print(arr1 / arr2) # [0.2 0.33333333 0.42857143 0.5 ] 除法
print(arr1 // arr2) # [0 0 0 0] 整除
print(arr1 % arr2) # [1 2 3 4] 取模
print(arr1 ** 2) # [ 1 4 9 16] 幂运算
# 标量运算
print(arr1 + 10) # [11 12 13 14]
print(arr1 * 2) # [2 4 6 8]
# 数学函数
print(np.add(arr1, arr2)) # 加法
print(np.subtract(arr1, arr2)) # 减法
print(np.multiply(arr1, arr2)) # 乘法
print(np.divide(arr1, arr2)) # 除法
print(np.power(arr1, 2)) # 幂运算
# 符号运算
print(np.abs(arr1)) # 绝对值
print(np.sqrt(arr1)) # 平方根
print(np.square(arr1)) # 平方
print(np.sign(arr1)) # 符号函数(-1,0,1)
print(np.negative(arr1)) # 取负
广播机制
# 广播: 不同形状数组的运算
arr1 = np.array([[1, 2, 3], [4, 5, 6]]) # (2,3)
arr2 = np.array([10, 20, 30]) # (3,)
# 自动扩展
print(arr1 + arr2) # (2,3) + (3,) -> (2,3)
# 标量广播
print(arr1 + 100) # 每个元素加100
# 二维广播
arr3 = np.array([[10], [20]]) # (2,1)
print(arr1 + arr3) # (2,3) + (2,1) -> (2,3)
# 广播规则
# 1. 从右到左比较维度
# 2. 维度相同或其中一个为1则兼容
# 3. 不兼容则报错
# 兼容示例
a = np.ones((3, 4)) # (3,4)
b = np.ones((3, 1)) # (3,1) -> (3,4)
c = np.ones((1, 4)) # (1,4) -> (3,4)
# 不兼容示例
# a = np.ones((3, 4))
# b = np.ones((4, 3)) # 错误: 无法广播
数学函数
arr = np.array([1, 2, 3, 4, 5])
# 三角函数
print(np.sin(arr)) # 正弦
print(np.cos(arr)) # 余弦
print(np.tan(arr)) # 正切
print(np.arcsin(arr)) # 反正弦
print(np.arccos(arr)) # 反余弦
print(np.arctan(arr)) # 反正切
# 指数和对数
print(np.exp(arr)) # e^x
print(np.exp2(arr)) # 2^x
print(np.log(arr)) # 自然对数
print(np.log10(arr)) # 常用对数
print(np.log2(arr)) # 以2为底对数
# 取整
arr2 = np.array([1.2, 2.5, 3.7, -1.8, -2.3])
print(np.round(arr2)) # 四舍五入
print(np.floor(arr2)) # 向下取整
print(np.ceil(arr2)) # 向上取整
print(np.trunc(arr2)) # 截断取整
# 其他
print(np.abs(arr)) # 绝对值
print(np.sqrt(arr)) # 平方根
print(np.square(arr)) # 平方
print(np.reciprocal(arr)) # 倒数
# 角度转换
deg = np.array([0, 90, 180, 270])
print(np.radians(deg)) # 转弧度
rad = np.radians(deg)
print(np.degrees(rad)) # 转角度
统计函数
arr = np.array([[1, 2, 3], [4, 5, 6]])
# 求和
print(arr.sum()) # 21 所有元素求和
print(arr.sum(axis=0)) # [5 7 9] 列求和
print(arr.sum(axis=1)) # [ 6 15] 行求和
# 平均值
print(arr.mean()) # 3.5 总平均
print(arr.mean(axis=0)) # [2.5 3.5 4.5] 列平均
print(arr.mean(axis=1)) # [2. 5.] 行平均
# 中位数
print(np.median(arr)) # 3.5
print(np.median(arr, axis=0)) # [2.5 3.5 4.5]
# 标准差和方差
print(arr.std()) # 标准差
print(arr.var()) # 方差
print(arr.std(axis=0)) # 列标准差
print(arr.var(axis=1)) # 行方差
# 最小最大
print(arr.min()) # 1 最小值
print(arr.max()) # 6 最大值
print(arr.min(axis=0)) # [1 2 3] 列最小值
print(arr.max(axis=1)) # [3 6] 行最大值
# 分位数
print(np.percentile(arr, 50)) # 50%分位数(中位数)
print(np.percentile(arr, [25, 50, 75])) # 多分位数
# 其他统计
print(np.argmin(arr)) # 最小值索引
print(np.argmax(arr)) # 最大值索引
print(arr.cumsum()) # 累计求和
print(arr.cumprod()) # 累计乘积
数组操作
reshape
arr = np.arange(12)
# 改变形状
arr2 = arr.reshape(3, 4) # 转为3行4列
arr3 = arr.reshape(2, 6) # 转为2行6列
arr4 = arr.reshape(3, -1) # 自动计算列数
# 展平
arr_flat = arr2.flatten() # 展平(返回副本)
arr_ravel = arr2.ravel() # 展平(返回视图,可能修改原数组)
# 转置
arr_t = arr2.T # 转置
arr_t = arr2.transpose() # 同上
# 维度交换
arr3 = np.arange(24).reshape(2, 3, 4)
arr_swap = arr3.swapaxes(0, 2) # 交换维度
arr_transpose = arr3.transpose(1, 0, 2) # 转置指定维度
concatenate
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
# 连接
result = np.concatenate([arr1, arr2]) # [1 2 3 4 5 6]
# 二维数组
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
# 按行连接(axis=0)
result = np.concatenate([arr1, arr2], axis=0)
# 按列连接(axis=1)
result = np.concatenate([arr1, arr2], axis=1)
# 堆叠
result = np.vstack([arr1, arr2]) # 垂直堆叠
result = np.hstack([arr1, arr2]) # 水平堆叠
result = np.stack([arr1, arr2]) # 创建新维度
# 列堆叠
result = np.column_stack([arr1, arr2])
# 行堆叠
result = np.row_stack([arr1, arr2])
split
arr = np.arange(12)
# 分割
result = np.split(arr, 3) # 分成3份
result = np.split(arr, [3, 7]) # 在指定位置分割
# 二维数组
arr2 = np.arange(12).reshape(4, 3)
# 按行分割
result = np.vsplit(arr2, 2) # 垂直分割
# 按列分割
result = np.hsplit(arr2, 3) # 水平分割
# array_split (不等分)
result = np.array_split(arr, 3) # 允许不等分
repeat/tile
arr = np.array([1, 2, 3])
# repeat: 重复元素
result = arr.repeat(2) # [1 1 2 2 3 3] 每个元素重复2次
result = arr.repeat([2, 3, 4]) # [1 1 2 2 2 3 3 3 3] 不同重复次数
# tile: 重复数组
result = np.tile(arr, 2) # [1 2 3 1 2 3] 数组重复2次
result = np.tile(arr, (2, 3)) # 2x3 网格重复
线性代数
矩阵运算
# 矩阵乘法
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
# 点积
result = np.dot(a, b) # 矩阵乘法
result = a @ b # 矩阵乘法(新语法)
# 逐元素乘法
result = a * b
# 转置
result = a.T
result = a.transpose()
# 共轭转置
result = a.conj().T
# 迹(对角线元素之和)
print(np.trace(a))
矩阵分解
import numpy.linalg as la
a = np.array([[1, 2], [3, 4]])
# 行列式
det = la.det(a) # -2.0
# 逆矩阵
inv = la.inv(a) # 逆矩阵
# 矩阵秩
rank = la.matrix_rank(a) # 2
# 特征值和特征向量
eigenvalues, eigenvectors = la.eig(a)
print(eigenvalues) # 特征值
print(eigenvectors) # 特征向量
# 奇异值分解
U, S, V = la.svd(a)
# QR 分解
Q, R = la.qr(a)
# Cholesky 分解
L = la.cholesky(a.T @ a)
解线性方程组
# Ax = b
A = np.array([[3, 1], [1, 2]])
b = np.array([9, 8])
# 解方程
x = la.solve(A, b) # [2. 3.]
# 最小二乘解
x, residuals, rank, s = la.lstsq(A, b, rcond=None)
范数和距离
a = np.array([1, 2, 3, 4])
# 范数
print(la.norm(a)) # L2范数(欧几里得范数)
print(la.norm(a, 1)) # L1范数
print(la.norm(a, np.inf)) # 无穷范数
# 矩阵范数
A = np.array([[1, 2], [3, 4]])
print(la.norm(A)) # Frobenius范数
print(la.norm(A, 'fro')) # Frobenius范数
print(la.norm(A, np.inf)) # 无穷范数
# 向量距离
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 欧几里得距离
dist = la.norm(a - b)
# 余弦相似度
cosine = np.dot(a, b) / (la.norm(a) * la.norm(b))
性能优化
向量化操作
# 避免 Python 循环
# 不推荐
result = []
for i in range(1000000):
result.append(i * 2)
# 推荐
arr = np.arange(1000000)
result = arr * 2 # 向量化操作
# 通用函数(ufunc)
arr = np.arange(10)
result = np.sqrt(arr) # 元素级操作
result = np.add(arr, 10) # 快速运算
内存优化
# 指定数据类型减少内存
arr = np.arange(10, dtype=np.int8) # 1字节
arr = np.arange(10, dtype=np.float32) # 4字节
# 避免不必要的复制
arr = np.arange(10)
view = arr.view() # 视图(共享内存)
copy = arr.copy() # 副本(独立内存)
# 原地操作
arr = np.arange(10)
arr *= 2 # 原地操作
arr = arr * 2 # 创建新数组
# 使用 out 参数
arr1 = np.arange(10)
arr2 = np.arange(10)
np.add(arr1, arr2, out=arr1) # 输出到已存在数组
广播优化
# 利用广播避免复制
a = np.arange(3).reshape(3, 1) # (3,1)
b = np.arange(3) # (3,)
result = a + b # 广播而非复制
# 避免不必要的维度扩展
a = np.arange(3)
result = a[:, np.newaxis] + np.arange(3) # 添加新轴
最佳实践
编码规范
# 导入惯例
import numpy as np
import numpy.linalg as la
# 类型提示
def process_array(arr: np.ndarray) -> np.ndarray:
return arr * 2
# 使用常量
PI = np.pi
E = np.e
# 避免魔法数字
arr = np.random.randn(1000)
mean = np.mean(arr)
std = np.std(arr)
threshold = mean + 2 * std
mask = arr > threshold
常见陷阱
# 1. 数组复制
arr = np.arange(10)
view = arr.view()
copy = arr.copy()
view[0] = 99 # 会影响原数组
copy[0] = 99 # 不影响原数组
# 2. 浮点数比较
# 不推荐
a = 0.1 + 0.2
b = 0.3
print(a == b) # False
# 推荐
print(np.isclose(a, b)) # True
# 3. 数组形状
arr = np.arange(10)
# arr.reshape(2, 5) # 正确
# arr.reshape(2, 6) # 错误:元素数量不匹配
# 4. 广播错误
a = np.ones((3, 4))
b = np.ones((4, 3))
# a + b # 错误:无法广播
a + b.T # 正确
# 5. 整数除法
a = np.array([1, 2, 3, 4])
result = a / 2 # 浮点数除法 [0.5 1. 1.5 2. ]
result = a // 2 # 整数除法 [0 1 1 2]
总结
NumPy 是 Python 科学计算的基础:
核心概念
- ndarray: 强大的N维数组对象
- 广播机制: 自动处理不同形状数组的运算
- 向量化: 高效的数组运算避免循环
- 视图机制: 共享内存的数组视图
主要功能
- 数组创建: 多种创建数组的方式
- 数组索引: 基本索引、切片、整数索引、布尔索引
- 数组运算: 算术运算、数学函数、统计函数
- 数组操作: reshape、连接、分割
- 线性代数: 矩阵运算、矩阵分解、解方程组
性能优势
- 比 Python 列表快 10-100 倍
- 内存占用更小
- 基于优化的 C/Fortran 库
- 向量化操作避免循环
- 广播机制减少内存复制
NumPy 是数据科学、机器学习、科学计算的基础,掌握 NumPy 是高效进行数值计算的关键。