NumPy 入门教程
目录
什么是 NumPy
NumPy(Numerical Python)是 Python 中用于科学计算的基础库,提供了高性能的多维数组对象和用于处理这些数组的工具。
核心特点
- ndarray:高效的多维数组对象
- 广播功能:对不同形状的数组进行算术运算
- 数学函数:丰富的线性代数、傅里叶变换、随机数等功能
- C/Fortran 集成:底层使用 C 语言实现,性能卓越
- 生态基础:Pandas、SciPy、Matplotlib、Scikit-learn 等库的基础
为什么使用 NumPy?
| 特性 | Python 列表 | NumPy 数组 |
|---|---|---|
| 性能 | 慢 | 快 10-100 倍 |
| 内存 | 占用大 | 占用小 |
| 功能 | 基础 | 丰富的数学函数 |
| 向量化 | 不支持 | 支持 |
| 广播 | 不支持 | 支持 |
# 性能对比示例
import numpy as np
import time
# Python 列表
start = time.time()
result_list = [x**2 for x in range(1000000)]
print(f"列表耗时: {time.time() - start:.4f}秒")
# NumPy 数组
start = time.time()
result_array = np.arange(1000000) ** 2
print(f"NumPy耗时: {time.time() - start:.4f}秒")
安装 NumPy
方法一:通过 Anaconda 安装
NumPy 已预装在 Anaconda 中,无需额外安装。
# 验证安装
python -c "import numpy; print(numpy.__version__)"
方法二:通过 pip 安装
pip install numpy
导入 NumPy
import numpy as np
# 查看版本
print(np.__version__)
约定俗成:始终使用 import numpy as np 作为标准导入方式。
NumPy 数组基础
ndarray 对象
NumPy 的核心是 ndarray(N-dimensional array)对象,它是一个多维数组。
import numpy as np
# 从列表创建数组
arr = np.array([1, 2, 3, 4, 5])
print(arr) # [1 2 3 4 5]
print(type(arr)) # <class 'numpy.ndarray'>
数组维度
# 0维数组(标量)
scalar = np.array(42)
print(scalar.ndim) # 0
# 1维数组(向量)
vector = np.array([1, 2, 3, 4, 5])
print(vector.ndim) # 1
# 2维数组(矩阵)
matrix = np.array([[1, 2, 3],
[4, 5, 6]])
print(matrix.ndim) # 2
# 3维数组
tensor = np.array([[[1, 2], [3, 4]],
[[5, 6], [7, 8]]])
print(tensor.ndim) # 3
数组创建
从现有数据创建
import numpy as np
# 从列表创建
arr1 = np.array([1, 2, 3, 4, 5])
# 从元组创建
arr2 = np.array((1, 2, 3, 4, 5))
# 从嵌套列表创建二维数组
arr3 = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 指定数据类型
arr4 = np.array([1, 2, 3], dtype=np.float64)
arr5 = np.array([1, 2, 3], dtype=np.int32)
常用创建函数
zeros - 全零数组
# 创建全零数组
zeros_1d = np.zeros(5) # [0. 0. 0. 0. 0.]
zeros_2d = np.zeros((3, 4)) # 3x4 的零矩阵
zeros_3d = np.zeros((2, 3, 4)) # 2x3x4 的零数组
# 指定数据类型
zeros_int = np.zeros(5, dtype=int) # [0 0 0 0 0]
ones - 全一数组
# 创建全一数组
ones_1d = np.ones(5) # [1. 1. 1. 1. 1.]
ones_2d = np.ones((3, 4)) # 3x4 的全一矩阵
full - 填充指定值
# 创建填充指定值的数组
full_arr = np.full((3, 4), 7) # 3x4 的数组,所有元素为 7
arange - 范围数组
# 类似 Python 的 range
arr1 = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
arr2 = np.arange(2, 10) # [2 3 4 5 6 7 8 9]
arr3 = np.arange(2, 10, 2) # [2 4 6 8]
arr4 = np.arange(0, 1, 0.1) # [0. 0.1 0.2 ... 0.9]
linspace - 等间距数组
# 在指定区间内生成等间距的点
arr1 = np.linspace(0, 10, 5) # [ 0. 2.5 5. 7.5 10. ]
arr2 = np.linspace(0, 1, 100) # 0到1之间的100个等间距点
# 不包含终点
arr3 = np.linspace(0, 10, 5, endpoint=False) # [0. 2. 4. 6. 8.]
eye / identity - 单位矩阵
# 创建单位矩阵
eye_arr = np.eye(4) # 4x4 单位矩阵
identity_arr = np.identity(3) # 3x3 单位矩阵
random - 随机数组
# 随机数组(详见后文随机数生成部分)
rand_arr = np.random.rand(3, 4) # 0-1 之间的均匀分布
randn_arr = np.random.randn(3, 4) # 标准正态分布
randint_arr = np.random.randint(0, 10, (3, 4)) # 0-9 的随机整数
empty - 未初始化数组
# 创建未初始化的数组(速度快,但值不确定)
empty_arr = np.empty((3, 4))
特殊数组创建
# 对角矩阵
diag_arr = np.diag([1, 2, 3, 4])
# 从现有数组创建副本
arr = np.array([1, 2, 3])
arr_copy = np.copy(arr)
# 创建与现有数组相同形状的新数组
original = np.array([[1, 2], [3, 4]])
zeros_like = np.zeros_like(original)
ones_like = np.ones_like(original)
数组属性
import numpy as np
arr = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# 维度数
print(arr.ndim) # 2
# 形状(行数,列数)
print(arr.shape) # (3, 4)
# 元素总数
print(arr.size) # 12
# 数据类型
print(arr.dtype) # int64(取决于系统)
# 每个元素的字节数
print(arr.itemsize) # 8
# 总字节数
print(arr.nbytes) # 96
# 数据类型名称
print(arr.dtype.name) # 'int64'
数据类型
NumPy 支持多种数据类型:
| 类型 | 说明 | 示例 |
|---|---|---|
int8, int16, int32, int64 | 有符号整数 | np.int32 |
uint8, uint16, uint32, uint64 | 无符号整数 | np.uint8 |
float16, float32, float64 | 浮点数 | np.float64 |
complex64, complex128 | 复数 | np.complex128 |
bool | 布尔值 | np.bool_ |
str_ | 字符串 | np.str_ |
object | Python 对象 | np.object_ |
# 数据类型转换
arr_int = np.array([1, 2, 3], dtype=np.int32)
arr_float = arr_int.astype(np.float64)
arr_bool = np.array([True, False, True])
# 查看数据类型信息
print(np.finfo(np.float64)) # 浮点数信息
print(np.iinfo(np.int32)) # 整数信息
数组索引和切片
一维数组索引
import numpy as np
arr = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
# 正向索引(从 0 开始)
print(arr[0]) # 10
print(arr[5]) # 60
# 负向索引(从 -1 开始)
print(arr[-1]) # 100
print(arr[-3]) # 80
# 修改元素
arr[0] = 999
print(arr[0]) # 999
一维数组切片
arr = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
# 基本切片 [start:stop:step]
print(arr[2:5]) # [30 40 50]
print(arr[:5]) # [10 20 30 40 50]
print(arr[5:]) # [60 70 80 90 100]
print(arr[::2]) # [10 30 50 70 90]
print(arr[1::2]) # [20 40 60 80 100]
print(arr[::-1]) # [100 90 80 70 60 50 40 30 20 10](反转)
# 注意:切片返回的是视图,不是副本
slice_arr = arr[2:5]
slice_arr[0] = 999
print(arr) # 原数组也被修改!
二维数组索引
arr_2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# 访问单个元素
print(arr_2d[0, 0]) # 1(第0行,第0列)
print(arr_2d[1, 2]) # 7(第1行,第2列)
print(arr_2d[2, -1]) # 12(最后一列)
# 访问整行
print(arr_2d[0]) # [1 2 3 4]
print(arr_2d[1]) # [5 6 7 8]
# 访问整列
print(arr_2d[:, 0]) # [1 5 9]
print(arr_2d[:, 2]) # [3 7 11]
二维数组切片
arr_2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# 切片语法 [row_start:row_stop, col_start:col_stop]
print(arr_2d[0:2, 1:3]) # [[2 3]
# [6 7]]
print(arr_2d[:2, :2]) # [[1 2]
# [5 6]]
print(arr_2d[1:, 2:]) # [[ 7 8]
# [11 12]]
# 步长切片
print(arr_2d[::2, ::2]) # [[ 1 3]
# [ 9 11]]
花式索引(Fancy Indexing)
arr = np.array([10, 20, 30, 40, 50, 60, 70, 80])
# 使用索引数组
indices = [0, 3, 5, 7]
print(arr[indices]) # [10 40 60 80]
# 使用布尔索引
mask = arr > 40
print(mask) # [False False False False True True True True]
print(arr[mask]) # [50 60 70 80]
# 组合条件
print(arr[(arr > 30) & (arr < 70)]) # [40 50 60]
print(arr[(arr < 20) | (arr > 70)]) # [10 80]
# 二维数组的花式索引
arr_2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
rows = [0, 2]
cols = [1, 2]
print(arr_2d[rows, cols]) # [2 9]
np.where 函数
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# 找到满足条件的索引
indices = np.where(arr > 5)
print(indices) # (array([5, 6, 7, 8, 9]),)
print(arr[indices]) # [ 6 7 8 9 10]
# 条件赋值
result = np.where(arr > 5, 'big', 'small')
print(result) # ['small' 'small' 'small' 'small' 'small' 'big' 'big' 'big' 'big' 'big']
数组操作
数组复制
arr = np.array([1, 2, 3, 4, 5])
# 视图(共享数据)
view_arr = arr.view()
view_arr[0] = 999
print(arr) # [999 2 3 4 5](原数组被修改)
# 副本(独立数据)
copy_arr = arr.copy()
copy_arr[0] = 888
print(arr) # [999 2 3 4 5](原数组不变)
print(copy_arr) # [888 2 3 4 5]
数组拼接
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
# 水平拼接
h_stack = np.hstack((arr1, arr2))
print(h_stack) # [1 2 3 4 5 6]
# 垂直拼接(需要二维)
arr1_2d = np.array([[1, 2, 3]])
arr2_2d = np.array([[4, 5, 6]])
v_stack = np.vstack((arr1_2d, arr2_2d))
print(v_stack) # [[1 2 3]
# [4 5 6]]
# 通用拼接
concat_h = np.concatenate((arr1, arr2), axis=0)
concat_v = np.concatenate((arr1_2d, arr2_2d), axis=0)
数组分割
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
# 等分
split_arr = np.split(arr, 3)
print(split_arr) # [array([1, 2, 3]), array([4, 5, 6]), array([7, 8, 9])]
# 在指定位置分割
split_arr = np.split(arr, [3, 6])
print(split_arr) # [array([1, 2, 3]), array([4, 5, 6]), array([7, 8, 9])]
# 水平和垂直分割(二维数组)
arr_2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
h_split = np.hsplit(arr_2d, 2)
v_split = np.vsplit(arr_2d, 3)
数组排序
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6, 5, 3])
# 排序(返回新数组)
sorted_arr = np.sort(arr)
print(sorted_arr) # [1 1 2 3 3 4 5 5 6 9]
# 原地排序
arr.sort()
print(arr) # [1 1 2 3 3 4 5 5 6 9]
# 降序排序
desc_arr = np.sort(arr)[::-1]
print(desc_arr) # [9 6 5 5 4 3 3 2 1 1]
# 获取排序索引
indices = np.argsort(arr)
print(indices) # [1 3 6 0 7 2 4 8 9 5]
去重
arr = np.array([1, 2, 2, 3, 3, 3, 4, 4, 5])
# 去重
unique_arr = np.unique(arr)
print(unique_arr) # [1 2 3 4 5]
# 返回计数
unique_vals, counts = np.unique(arr, return_counts=True)
print(unique_vals) # [1 2 3 4 5]
print(counts) # [1 2 3 2 1]
数学运算
基本算术运算
import numpy as np
a = np.array([1, 2, 3, 4, 5])
b = np.array([10, 20, 30, 40, 50])
# 加法
print(a + b) # [11 22 33 44 55]
print(np.add(a, b)) # [11 22 33 44 55]
# 减法
print(b - a) # [ 9 18 27 36 45]
print(np.subtract(b, a))
# 乘法(元素级)
print(a * b) # [ 10 40 90 160 250]
print(np.multiply(a, b))
# 除法
print(b / a) # [10. 10. 10. 10. 10.]
print(np.divide(b, a))
# 幂运算
print(a ** 2) # [ 1 4 9 16 25]
print(np.power(a, 2))
# 取模
print(b % a) # [0 0 0 0 0]
print(np.mod(b, a))
标量运算
arr = np.array([1, 2, 3, 4, 5])
print(arr + 10) # [11 12 13 14 15]
print(arr * 2) # [ 2 4 6 8 10]
print(arr ** 2) # [ 1 4 9 16 25]
print(arr / 2) # [0.5 1. 1.5 2. 2.5]
矩阵乘法
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
# 矩阵乘法
result = np.dot(A, B)
print(result) # [[19 22]
# [43 50]]
# 或使用 @ 运算符(Python 3.5+)
result = A @ B
# 或使用 matmul
result = np.matmul(A, B)
比较运算
a = np.array([1, 2, 3, 4, 5])
b = np.array([5, 4, 3, 2, 1])
print(a > b) # [False False True True True]
print(a == b) # [False False True False False]
print(a >= 3) # [False False True True True]
print(a != b) # [ True True False True True]
逻辑运算
a = np.array([True, False, True, False])
b = np.array([True, True, False, False])
print(np.logical_and(a, b)) # [ True False False False]
print(np.logical_or(a, b)) # [ True True True False]
print(np.logical_not(a)) # [False True False True]
print(np.logical_xor(a, b)) # [False True True False]
三角函数
angles = np.array([0, np.pi/6, np.pi/4, np.pi/3, np.pi/2])
print(np.sin(angles)) # 正弦
print(np.cos(angles)) # 余弦
print(np.tan(angles)) # 正切
# 反三角函数
print(np.arcsin(1)) # π/2
print(np.arccos(0)) # π/2
print(np.arctan(1)) # π/4
指数和对数
arr = np.array([1, 2, 3, 4, 5])
# 指数
print(np.exp(arr)) # e^x
print(np.exp2(arr)) # 2^x
print(np.power(2, arr)) # 2^x
# 对数
print(np.log(arr)) # 自然对数 ln(x)
print(np.log2(arr)) # log2(x)
print(np.log10(arr)) # log10(x)
舍入函数
arr = np.array([1.2, 2.5, 3.7, -1.2, -2.5, -3.7])
print(np.round(arr)) # 四舍五入 [ 1. 2. 4. -1. -2. -4.]
print(np.floor(arr)) # 向下取整 [ 1. 2. 3. -2. -3. -4.]
print(np.ceil(arr)) # 向上取整 [ 2. 3. 4. -1. -2. -3.]
print(np.trunc(arr)) # 截断小数 [ 1. 2. 3. -1. -2. -3.]
统计函数
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
arr_2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
基本统计
# 求和
print(np.sum(arr)) # 55
print(np.sum(arr_2d)) # 45
print(np.sum(arr_2d, axis=0)) # [12 15 18](按列求和)
print(np.sum(arr_2d, axis=1)) # [ 6 15 24](按行求和)
# 平均值
print(np.mean(arr)) # 5.5
print(np.mean(arr_2d, axis=0))
# 中位数
print(np.median(arr)) # 5.5
# 最大值和最小值
print(np.max(arr)) # 10
print(np.min(arr)) # 1
print(np.argmax(arr)) # 9(最大值的索引)
print(np.argmin(arr)) # 0(最小值的索引)
# 极差
print(np.ptp(arr)) # 9(max - min)
方差和标准差
# 方差
print(np.var(arr)) # 方差
# 标准差
print(np.std(arr)) # 标准差
# 样本方差和标准差(ddof=1)
print(np.var(arr, ddof=1))
print(np.std(arr, ddof=1))
百分位数
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(np.percentile(arr, 50)) # 50% 分位数(中位数)
print(np.percentile(arr, 25)) # 25% 分位数
print(np.percentile(arr, 75)) # 75% 分位数
print(np.percentile(arr, [25, 50, 75])) # 多个分位数
累积运算
arr = np.array([1, 2, 3, 4, 5])
# 累积和
print(np.cumsum(arr)) # [ 1 3 6 10 15]
# 累积积
print(np.cumprod(arr)) # [ 1 2 6 24 120]
# 累积最大值
print(np.maximum.accumulate(arr)) # [1 2 3 4 5]
# 累积最小值
print(np.minimum.accumulate(arr)) # [1 2 3 4 5]
相关性
x = np.array([1, 2, 3, 4, 5])
y = np.array([2, 4, 5, 4, 5])
# 相关系数矩阵
corr_matrix = np.corrcoef(x, y)
print(corr_matrix)
# 协方差矩阵
cov_matrix = np.cov(x, y)
print(cov_matrix)
数组形状操作
重塑(Reshape)
import numpy as np
arr = np.arange(12) # [0 1 2 3 4 5 6 7 8 9 10 11]
# 重塑为 3x4 矩阵
reshaped = arr.reshape(3, 4)
print(reshaped)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# 重塑为 2x6 矩阵
reshaped2 = arr.reshape(2, 6)
# 自动推断维度(使用 -1)
reshaped3 = arr.reshape(3, -1) # 自动计算第二维为 4
reshaped4 = arr.reshape(-1, 4) # 自动计算第一维为 3
# 展平
flattened = reshaped.flatten() # 返回副本
raveled = reshaped.ravel() # 返回视图(更快)
转置
arr_2d = np.array([[1, 2, 3],
[4, 5, 6]])
# 转置
transposed = arr_2d.T
print(transposed)
# [[1 4]
# [2 5]
# [3 6]]
# 或使用 transpose
transposed2 = np.transpose(arr_2d)
# 高维数组的轴交换
arr_3d = np.random.rand(2, 3, 4)
swapped = np.transpose(arr_3d, (2, 0, 1)) # 交换轴
增加/减少维度
arr = np.array([1, 2, 3, 4, 5])
# 增加维度
arr_2d = arr[np.newaxis, :] # 形状: (1, 5)
arr_2d_v = arr[:, np.newaxis] # 形状: (5, 1)
# 或使用 reshape
arr_2d = arr.reshape(1, -1)
arr_2d_v = arr.reshape(-1, 1)
# 或使用 expand_dims
arr_2d = np.expand_dims(arr, axis=0)
arr_2d_v = np.expand_dims(arr, axis=1)
# 减少维度
arr_squeezed = np.squeeze(arr_2d) # 移除大小为 1 的维度
堆叠
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 垂直堆叠
v_stack = np.vstack((a, b))
print(v_stack)
# [[1 2 3]
# [4 5 6]]
# 水平堆叠
h_stack = np.hstack((a, b))
print(h_stack) # [1 2 3 4 5 6]
# 深度堆叠(3维)
d_stack = np.dstack((a, b))
# 通用堆叠
stack_v = np.stack((a, b), axis=0)
stack_h = np.stack((a, b), axis=1)
线性代数
NumPy 提供了强大的线性代数功能(np.linalg 模块)。
import numpy as np
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
矩阵运算
# 矩阵乘法
C = np.dot(A, B)
C = A @ B
C = np.matmul(A, B)
# 矩阵幂
A_squared = np.linalg.matrix_power(A, 2)
# 转置
A_T = A.T
行列式和逆矩阵
# 行列式
det_A = np.linalg.det(A)
print(det_A) # -2.0
# 逆矩阵
if det_A != 0:
A_inv = np.linalg.inv(A)
print(A_inv)
# 验证:A * A_inv = I
identity = A @ A_inv
print(np.round(identity)) # [[1. 0.]
# [0. 1.]]
特征值和特征向量
# 特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(A)
print("特征值:", eigenvalues)
print("特征向量:\n", eigenvectors)
解线性方程组
# 解 Ax = b
A = np.array([[2, 1],
[1, 3]])
b = np.array([8, 13])
x = np.linalg.solve(A, b)
print(x) # [1. 6.]
# 验证
print(A @ x) # [ 8. 13.]
其他线性代数函数
# 秩
rank = np.linalg.matrix_rank(A)
# 迹(对角线元素之和)
trace = np.trace(A)
# 范数
norm = np.linalg.norm(A)
norm_fro = np.linalg.norm(A, 'fro') # Frobenius 范数
# QR 分解
Q, R = np.linalg.qr(A)
# SVD 分解
U, S, Vt = np.linalg.svd(A)
# Cholesky 分解(对称正定矩阵)
A_sym = np.array([[4, 2],
[2, 3]])
L = np.linalg.cholesky(A_sym)
随机数生成
NumPy 的 random 模块提供了丰富的随机数生成功能。
新版 API(推荐)
import numpy as np
# 创建随机数生成器
rng = np.random.default_rng(seed=42)
# 均匀分布 [0, 1)
random_uniform = rng.random(5)
print(random_uniform)
# 指定范围的均匀分布
random_range = rng.uniform(0, 10, 5)
print(random_range)
# 标准正态分布
random_normal = rng.standard_normal(5)
print(random_normal)
# 指定均值和标准差的正态分布
random_norm = rng.normal(loc=0, scale=1, size=5)
print(random_norm)
# 随机整数
random_int = rng.integers(0, 10, 5)
print(random_int)
# 从数组中随机选择
choices = rng.choice([1, 2, 3, 4, 5], 3, replace=False)
print(choices)
# 洗牌
arr = np.array([1, 2, 3, 4, 5])
rng.shuffle(arr)
print(arr)
旧版 API(仍可使用)
# 均匀分布
np.random.rand(3, 4) # [0, 1) 均匀分布
np.random.random((3, 4))
# 标准正态分布
np.random.randn(3, 4)
# 随机整数
np.random.randint(0, 10, (3, 4))
# 设置种子
np.random.seed(42)
常用分布
rng = np.random.default_rng()
# 二项分布
binomial = rng.binomial(n=10, p=0.5, size=1000)
# 泊松分布
poisson = rng.poisson(lam=5, size=1000)
# 指数分布
exponential = rng.exponential(scale=2.0, size=1000)
# 伽马分布
gamma = rng.gamma(shape=2.0, scale=2.0, size=1000)
# Beta 分布
beta = rng.beta(a=2, b=5, size=1000)
广播机制
广播(Broadcasting)是 NumPy 最强大的特性之一,允许不同形状的数组进行算术运算。
广播规则
- 如果数组维度数不同,较小的数组在前面补 1
- 如果某个维度大小不同,且其中一个为 1,则该维度可以扩展
- 如果维度大小不同且都不为 1,则报错
示例
import numpy as np
# 示例 1:标量与数组
arr = np.array([1, 2, 3, 4, 5])
result = arr + 10
print(result) # [11 12 13 14 15]
# 示例 2:一维数组与二维数组
arr_2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
arr_1d = np.array([10, 20, 30])
result = arr_2d + arr_1d
print(result)
# [[11 22 33]
# [14 25 36]
# [17 28 39]]
# 示例 3:列向量与行向量
col = np.array([[1],
[2],
[3]]) # 形状: (3, 1)
row = np.array([10, 20, 30]) # 形状: (3,)
result = col + row
print(result)
# [[11 21 31]
# [12 22 32]
# [13 23 33]]
# 示例 4:不兼容的形状(会报错)
try:
arr1 = np.array([1, 2, 3])
arr2 = np.array([1, 2])
result = arr1 + arr2 # ValueError!
except ValueError as e:
print(f"错误: {e}")
广播应用
# 标准化数据
data = np.random.randn(100, 5)
mean = np.mean(data, axis=0)
std = np.std(data, axis=0)
normalized = (data - mean) / std
# 外积
a = np.array([1, 2, 3])
b = np.array([10, 20, 30, 40])
outer = a[:, np.newaxis] * b
print(outer)
# [[ 10 20 30 40]
# [ 20 40 60 80]
# [ 30 60 90 120]]
性能优化
向量化运算
import numpy as np
import time
# ❌ 避免:使用循环
def slow_sum(arr):
result = 0
for x in arr:
result += x ** 2
return result
# ✅ 推荐:使用向量化
def fast_sum(arr):
return np.sum(arr ** 2)
arr = np.random.rand(1000000)
start = time.time()
slow_sum(arr)
print(f"循环耗时: {time.time() - start:.4f}秒")
start = time.time()
fast_sum(arr)
print(f"向量化耗时: {time.time() - start:.4f}秒")
使用内置函数
# ❌ 避免
result = np.array([x * 2 for x in arr])
# ✅ 推荐
result = arr * 2
# ❌ 避免
result = np.array([np.sqrt(x) for x in arr])
# ✅ 推荐
result = np.sqrt(arr)
内存优化
# 选择合适的数据类型
arr_float64 = np.array([1, 2, 3], dtype=np.float64) # 8 字节/元素
arr_float32 = np.array([1, 2, 3], dtype=np.float32) # 4 字节/元素
arr_int16 = np.array([1, 2, 3], dtype=np.int16) # 2 字节/元素
# 使用视图而非副本
view = arr[:] # 视图,不复制数据
copy = arr.copy() # 副本,复制数据
# 就地操作
arr += 1 # 就地加法,节省内存
arr = arr + 1 # 创建新数组
批量处理
# 处理大数据集时分批处理
def process_in_batches(data, batch_size=1000):
results = []
for i in range(0, len(data), batch_size):
batch = data[i:i+batch_size]
result = np.sum(batch ** 2)
results.append(result)
return np.sum(results)
实战案例
案例 1:图像处理基础
import numpy as np
import matplotlib.pyplot as plt
# 创建示例图像(100x100 像素,RGB)
image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
# 转换为灰度图
gray_image = np.mean(image, axis=2).astype(np.uint8)
# 调整亮度
brighter = np.clip(image + 50, 0, 255).astype(np.uint8)
# 翻转图像
flipped_h = np.flip(image, axis=1) # 水平翻转
flipped_v = np.flip(image, axis=0) # 垂直翻转
# 裁剪
cropped = image[25:75, 25:75, :]
print(f"原始图像形状: {image.shape}")
print(f"灰度图形状: {gray_image.shape}")
案例 2:数据统计分析
import numpy as np
# 生成模拟数据
np.random.seed(42)
scores = np.random.normal(loc=75, scale=10, size=1000)
# 基本统计
print(f"平均分: {np.mean(scores):.2f}")
print(f"中位数: {np.median(scores):.2f}")
print(f"标准差: {np.std(scores):.2f}")
print(f"最高分: {np.max(scores):.2f}")
print(f"最低分: {np.min(scores):.2f}")
# 分数分布
bins = np.arange(0, 101, 10)
hist, bin_edges = np.histogram(scores, bins=bins)
print("\n分数分布:")
for i in range(len(hist)):
print(f"{bin_edges[i]:.0f}-{bin_edges[i+1]:.0f}: {hist[i]}人")
# 百分位数
print(f"\n前 10% 分数线: {np.percentile(scores, 90):.2f}")
print(f"前 25% 分数线: {np.percentile(scores, 75):.2f}")
案例 3:矩阵运算求解方程组
import numpy as np
# 三元一次方程组
# 2x + y - z = 8
# -3x - y + 2z = -11
# -2x + y + 2z = -3
A = np.array([[2, 1, -1],
[-3, -1, 2],
[-2, 1, 2]])
b = np.array([8, -11, -3])
# 求解
solution = np.linalg.solve(A, b)
print(f"x = {solution[0]:.2f}")
print(f"y = {solution[1]:.2f}")
print(f"z = {solution[2]:.2f}")
# 验证
print(f"\n验证: {A @ solution}")
print(f"期望: {b}")
案例 4:蒙特卡洛模拟估算 π
import numpy as np
def estimate_pi(num_samples=1000000):
"""使用蒙特卡洛方法估算 π"""
# 在 [0,1] x [0,1] 正方形内随机生成点
points = np.random.random((num_samples, 2))
# 计算每个点到原点的距离
distances = np.sqrt(points[:, 0]**2 + points[:, 1]**2)
# 统计在单位圆内的点
inside_circle = np.sum(distances <= 1)
# π ≈ 4 * (圆内点数 / 总点数)
pi_estimate = 4 * inside_circle / num_samples
return pi_estimate
pi_est = estimate_pi(1000000)
print(f"估算的 π 值: {pi_est:.6f}")
print(f"实际 π 值: {np.pi:.6f}")
print(f"误差: {abs(pi_est - np.pi):.6f}")
案例 5:股票收益率分析
import numpy as np
# 模拟股票价格(30天)
np.random.seed(42)
days = 30
initial_price = 100
daily_returns = np.random.normal(0.001, 0.02, days)
prices = initial_price * np.cumprod(1 + daily_returns)
# 计算日收益率
returns = np.diff(prices) / prices[:-1]
# 统计分析
print(f"初始价格: ${prices[0]:.2f}")
print(f"最终价格: ${prices[-1]:.2f}")
print(f"总收益率: {(prices[-1] - prices[0]) / prices[0] * 100:.2f}%")
print(f"平均日收益率: {np.mean(returns) * 100:.4f}%")
print(f"日收益率标准差: {np.std(returns) * 100:.4f}%")
print(f"最高价格: ${np.max(prices):.2f}")
print(f"最低价格: ${np.min(prices):.2f}")
# 累计收益率
cumulative_returns = (prices - prices[0]) / prices[0] * 100
print(f"\n每日累计收益率:")
for i, ret in enumerate(cumulative_returns):
print(f"第{i+1}天: {ret:.2f}%")
总结
核心知识点回顾
- ✅ 数组创建:
np.array(),np.zeros(),np.ones(),np.arange(),np.linspace() - ✅ 数组属性:
.ndim,.shape,.size,.dtype - ✅ 索引切片:基本索引、切片、花式索引、布尔索引
- ✅ 数学运算:算术运算、矩阵乘法、三角函数、指数对数
- ✅ 统计函数:
sum(),mean(),std(),max(),min() - ✅ 形状操作:
reshape(),transpose(),flatten(),stack() - ✅ 线性代数:
linalg模块的各种矩阵运算 - ✅ 随机数:
random模块生成各种分布的随机数 - ✅ 广播机制:不同形状数组的自动对齐运算
学习建议
- 多练习:通过实际项目巩固知识
- 理解广播:广播是 NumPy 的核心,务必深入理解
- 向量化思维:避免使用循环,充分利用向量化运算
- 查阅文档:NumPy 官方文档非常详细,善用搜索
- 结合应用:与 Pandas、Matplotlib 结合使用
下一步
掌握 NumPy 后,建议继续学习:
- Pandas:数据处理和分析
- Matplotlib:数据可视化
- SciPy:科学计算
- Scikit-learn:机器学习
参考资料
- NumPy 官方文档:numpy.org/doc/
- NumPy 用户指南:numpy.org/doc/stable/…
- NumPy 快速入门:numpy.org/doc/stable/…