📚 本章概述
本章将深入探讨NumPy的数学运算和统计函数,这是NumPy最强大的功能之一。您将学习通用函数(ufunc)的概念、各种数学运算操作,以及丰富的统计分析函数,为数据分析和科学计算打下坚实基础。
🎯 学习目标
- 理解通用函数(ufunc)的概念和优势
- 掌握基本数学运算和高级数学函数
- 学会使用统计函数进行数据分析
- 了解数组比较和逻辑运算
- 掌握聚合函数和约简操作
1. 通用函数(Universal Functions)
1.1 什么是通用函数?
通用函数(ufunc)是对数组中每个元素进行操作的函数,支持向量化运算、广播和其他标准特性。
import numpy as np
import matplotlib.pyplot as plt
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
print("🔢 通用函数示例")
print("=" * 50)
# 创建测试数组
arr = np.array([1, 2, 3, 4, 5])
print(f"原始数组: {arr}")
# 平方根函数
sqrt_arr = np.sqrt(arr)
print(f"平方根: {sqrt_arr}")
# 指数函数
exp_arr = np.exp(arr)
print(f"指数函数: {exp_arr}")
# 对数函数
log_arr = np.log(arr)
print(f"自然对数: {log_arr}")
1.2 ufunc的特性
# 1. 向量化运算
print("\n📊 向量化运算对比:")
# 传统Python循环
def python_sqrt(arr):
result = []
for x in arr:
result.append(x ** 0.5)
return result
# NumPy向量化
large_arr = np.random.random(1000000)
# 性能对比
import time
# Python方式
start = time.time()
result_python = python_sqrt(large_arr[:1000]) # 只测试1000个元素
python_time = time.time() - start
# NumPy方式
start = time.time()
result_numpy = np.sqrt(large_arr)
numpy_time = time.time() - start
print(f"Python循环时间 (1000元素): {python_time:.6f}秒")
print(f"NumPy向量化时间 (1000000元素): {numpy_time:.6f}秒")
print(f"NumPy速度提升: {python_time/numpy_time*1000:.1f}倍")
1.3 常用的一元ufunc
# 2. 常用一元通用函数
print("\n🧮 常用一元通用函数:")
x = np.array([-2, -1, 0, 1, 2, 3])
print(f"原始数组: {x}")
# 绝对值
print(f"绝对值 np.abs(): {np.abs(x)}")
# 符号函数
print(f"符号函数 np.sign(): {np.sign(x)}")
# 平方
print(f"平方 np.square(): {np.square(x)}")
# 三角函数
angles = np.array([0, np.pi/6, np.pi/4, np.pi/3, np.pi/2])
print(f"\n角度数组: {angles}")
print(f"正弦值: {np.sin(angles)}")
print(f"余弦值: {np.cos(angles)}")
print(f"正切值: {np.tan(angles)}")
# 反三角函数
print(f"反正弦: {np.arcsin([0, 0.5, 1])}")
print(f"反余弦: {np.arccos([0, 0.5, 1])}")
1.4 常用的二元ufunc
# 3. 二元通用函数
print("\n🔄 二元通用函数:")
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
print(f"数组a: {a}")
print(f"数组b: {b}")
# 基本运算
print(f"加法 a + b: {np.add(a, b)}")
print(f"减法 a - b: {np.subtract(a, b)}")
print(f"乘法 a * b: {np.multiply(a, b)}")
print(f"除法 a / b: {np.divide(a, b)}")
# 幂运算
print(f"幂运算 a ** b: {np.power(a, b)}")
# 取模运算
print(f"取模 a % 3: {np.mod(a, 3)}")
# 最大值和最小值
print(f"逐元素最大值: {np.maximum(a, b)}")
print(f"逐元素最小值: {np.minimum(a, b)}")
2. 数学运算
2.1 基本算术运算
# 4. 基本算术运算
print("\n➕ 基本算术运算:")
# 创建测试矩阵
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print("矩阵A:")
print(A)
print("矩阵B:")
print(B)
# 逐元素运算
print(f"\nA + B (逐元素加法):")
print(A + B)
print(f"A * B (逐元素乘法):")
print(A * B)
print(f"A / B (逐元素除法):")
print(A / B)
# 矩阵乘法
print(f"A @ B (矩阵乘法):")
print(A @ B)
# 或者使用np.dot
print(f"np.dot(A, B) (矩阵乘法):")
print(np.dot(A, B))
2.2 数学函数
# 5. 高级数学函数
print("\n📐 高级数学函数:")
# 创建测试数据
x = np.linspace(0, 2*np.pi, 100)
y = np.linspace(-5, 5, 11)
# 三角函数应用
print("三角函数应用:")
sin_values = np.sin(x)
cos_values = np.cos(x)
print(f"sin函数值范围: [{sin_values.min():.3f}, {sin_values.max():.3f}]")
print(f"cos函数值范围: [{cos_values.min():.3f}, {cos_values.max():.3f}]")
# 指数和对数函数
print(f"\n指数和对数函数:")
print(f"e^2 = {np.exp(2):.3f}")
print(f"ln(e) = {np.log(np.e):.3f}")
print(f"log10(100) = {np.log10(100):.3f}")
print(f"log2(8) = {np.log2(8):.3f}")
# 双曲函数
print(f"\n双曲函数:")
print(f"sinh(1) = {np.sinh(1):.3f}")
print(f"cosh(1) = {np.cosh(1):.3f}")
print(f"tanh(1) = {np.tanh(1):.3f}")
2.3 舍入函数
# 6. 舍入函数
print("\n🔄 舍入函数:")
values = np.array([-2.7, -1.5, -0.3, 0.3, 1.5, 2.7])
print(f"原始值: {values}")
print(f"向下取整 floor: {np.floor(values)}")
print(f"向上取整 ceil: {np.ceil(values)}")
print(f"四舍五入 round: {np.round(values)}")
print(f"截断取整 trunc: {np.trunc(values)}")
# 指定小数位数的舍入
decimal_values = np.array([3.14159, 2.71828, 1.41421])
print(f"\n原始值: {decimal_values}")
print(f"保留2位小数: {np.round(decimal_values, 2)}")
print(f"保留3位小数: {np.round(decimal_values, 3)}")
3. 统计函数
3.1 基本统计量
# 7. 基本统计函数
print("\n📊 基本统计函数:")
# 创建测试数据
np.random.seed(42)
data = np.random.normal(100, 15, 1000) # 均值100,标准差15的正态分布
print(f"数据形状: {data.shape}")
print(f"数据类型: {data.dtype}")
# 中心趋势
print(f"\n中心趋势:")
print(f"均值: {np.mean(data):.2f}")
print(f"中位数: {np.median(data):.2f}")
# 离散程度
print(f"\n离散程度:")
print(f"标准差: {np.std(data):.2f}")
print(f"方差: {np.var(data):.2f}")
print(f"最小值: {np.min(data):.2f}")
print(f"最大值: {np.max(data):.2f}")
print(f"极差: {np.ptp(data):.2f}") # peak to peak
# 分位数
print(f"\n分位数:")
percentiles = [25, 50, 75, 90, 95, 99]
for p in percentiles:
value = np.percentile(data, p)
print(f"{p}%分位数: {value:.2f}")
3.2 多维数组统计
# 8. 多维数组统计
print("\n📈 多维数组统计:")
# 创建2D数组
matrix = np.random.randint(1, 100, (5, 4))
print("测试矩阵:")
print(matrix)
# 全局统计
print(f"\n全局统计:")
print(f"总和: {np.sum(matrix)}")
print(f"均值: {np.mean(matrix):.2f}")
print(f"标准差: {np.std(matrix):.2f}")
# 按轴统计
print(f"\n按轴统计:")
print(f"按行求和 (axis=1): {np.sum(matrix, axis=1)}")
print(f"按列求和 (axis=0): {np.sum(matrix, axis=0)}")
print(f"按行求均值 (axis=1): {np.mean(matrix, axis=1)}")
print(f"按列求均值 (axis=0): {np.mean(matrix, axis=0)}")
# 累积统计
print(f"\n累积统计:")
print(f"累积和: {np.cumsum(matrix.flatten())[:10]}...") # 只显示前10个
print(f"累积乘积: {np.cumprod([1, 2, 3, 4, 5])}")
3.3 相关性分析
# 9. 相关性分析
print("\n🔗 相关性分析:")
# 创建相关数据
np.random.seed(42)
x = np.random.normal(0, 1, 100)
y = 2 * x + np.random.normal(0, 0.5, 100) # y与x正相关
z = np.random.normal(0, 1, 100) # z与x无关
# 计算相关系数
corr_xy = np.corrcoef(x, y)[0, 1]
corr_xz = np.corrcoef(x, z)[0, 1]
print(f"x与y的相关系数: {corr_xy:.3f}")
print(f"x与z的相关系数: {corr_xz:.3f}")
# 协方差矩阵
data_matrix = np.array([x, y, z])
cov_matrix = np.cov(data_matrix)
print(f"\n协方差矩阵:")
print(cov_matrix)
# 相关系数矩阵
corr_matrix = np.corrcoef(data_matrix)
print(f"\n相关系数矩阵:")
print(corr_matrix)
4. 数组比较和逻辑运算
4.1 比较运算
# 10. 数组比较运算
print("\n⚖️ 数组比较运算:")
a = np.array([1, 2, 3, 4, 5])
b = np.array([1, 3, 2, 4, 6])
print(f"数组a: {a}")
print(f"数组b: {b}")
# 逐元素比较
print(f"a == b: {a == b}")
print(f"a != b: {a != b}")
print(f"a < b: {a < b}")
print(f"a > b: {a > b}")
print(f"a <= b: {a <= b}")
print(f"a >= b: {a >= b}")
# 数组级比较
print(f"\n数组级比较:")
print(f"数组相等 array_equal: {np.array_equal(a, b)}")
print(f"数组近似相等 allclose: {np.allclose(a, b, atol=1)}")
4.2 逻辑运算
# 11. 逻辑运算
print("\n🔀 逻辑运算:")
# 布尔数组
bool1 = np.array([True, False, True, False])
bool2 = np.array([True, True, False, False])
print(f"bool1: {bool1}")
print(f"bool2: {bool2}")
# 逻辑运算
print(f"逻辑与 &: {bool1 & bool2}")
print(f"逻辑或 |: {bool1 | bool2}")
print(f"逻辑异或 ^: {bool1 ^ bool2}")
print(f"逻辑非 ~: {~bool1}")
# 条件选择
arr = np.array([1, 2, 3, 4, 5])
condition = arr > 3
result = np.where(condition, arr, 0)
print(f"\n条件选择:")
print(f"原数组: {arr}")
print(f"条件 > 3: {condition}")
print(f"条件选择结果: {result}")
4.3 集合运算
# 12. 集合运算
print("\n🎯 集合运算:")
set1 = np.array([1, 2, 3, 4, 5])
set2 = np.array([3, 4, 5, 6, 7])
print(f"集合1: {set1}")
print(f"集合2: {set2}")
# 集合运算
print(f"交集: {np.intersect1d(set1, set2)}")
print(f"并集: {np.union1d(set1, set2)}")
print(f"差集 (set1 - set2): {np.setdiff1d(set1, set2)}")
print(f"对称差集: {np.setxor1d(set1, set2)}")
# 成员检测
print(f"3 在 set1 中: {np.isin(3, set1)}")
print(f"set1 中哪些元素在 set2 中: {np.isin(set1, set2)}")
5. 聚合函数和约简操作
5.1 基本聚合函数
# 13. 聚合函数
print("\n📊 聚合函数:")
# 创建测试数据
data = np.random.randint(1, 100, (4, 5))
print("测试数据:")
print(data)
# 基本聚合
print(f"\n基本聚合:")
print(f"总和: {np.sum(data)}")
print(f"乘积: {np.prod(data)}")
print(f"均值: {np.mean(data):.2f}")
print(f"最小值: {np.min(data)}")
print(f"最大值: {np.max(data)}")
# 按轴聚合
print(f"\n按轴聚合:")
print(f"按行求和: {np.sum(data, axis=1)}")
print(f"按列求和: {np.sum(data, axis=0)}")
print(f"按行求最大值: {np.max(data, axis=1)}")
print(f"按列求最小值: {np.min(data, axis=0)}")
5.2 位置函数
# 14. 位置函数
print("\n📍 位置函数:")
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6])
print(f"数组: {arr}")
# 查找位置
print(f"最大值位置: {np.argmax(arr)}")
print(f"最小值位置: {np.argmin(arr)}")
print(f"非零元素位置: {np.nonzero(arr)}")
# 排序相关
print(f"排序后的索引: {np.argsort(arr)}")
print(f"排序后的数组: {arr[np.argsort(arr)]}")
# 多维数组的位置
matrix = np.random.randint(1, 10, (3, 3))
print(f"\n矩阵:")
print(matrix)
max_pos = np.unravel_index(np.argmax(matrix), matrix.shape)
print(f"最大值位置 (行, 列): {max_pos}")
print(f"最大值: {matrix[max_pos]}")
6. 实际应用案例
6.1 数据分析案例
# 15. 学生成绩分析案例
print("\n🎓 学生成绩分析案例:")
# 模拟学生成绩数据
np.random.seed(42)
n_students = 100
n_subjects = 5
# 生成成绩数据 (0-100分)
scores = np.random.normal(75, 12, (n_students, n_subjects))
scores = np.clip(scores, 0, 100) # 限制在0-100范围内
subject_names = ['数学', '语文', '英语', '物理', '化学']
print(f"学生数量: {n_students}")
print(f"科目: {subject_names}")
# 基本统计
print(f"\n各科目统计:")
for i, subject in enumerate(subject_names):
mean_score = np.mean(scores[:, i])
std_score = np.std(scores[:, i])
min_score = np.min(scores[:, i])
max_score = np.max(scores[:, i])
print(f"{subject}: 均分{mean_score:.1f}, 标准差{std_score:.1f}, "
f"最低{min_score:.1f}, 最高{max_score:.1f}")
# 学生总分排名
total_scores = np.sum(scores, axis=1)
ranking = np.argsort(total_scores)[::-1] # 降序排列
print(f"\n前5名学生:")
for i in range(5):
student_id = ranking[i]
total = total_scores[student_id]
avg = np.mean(scores[student_id])
print(f"学生{student_id+1}: 总分{total:.1f}, 平均分{avg:.1f}")
# 科目相关性分析
print(f"\n科目相关性矩阵:")
correlation_matrix = np.corrcoef(scores.T)
print(" ", end="")
for name in subject_names:
print(f"{name:>6}", end="")
print()
for i, name in enumerate(subject_names):
print(f"{name:>4} ", end="")
for j in range(len(subject_names)):
print(f"{correlation_matrix[i, j]:>6.2f}", end="")
print()
6.2 金融数据分析
# 16. 股票价格分析案例
print("\n💰 股票价格分析案例:")
# 模拟股票价格数据
np.random.seed(42)
days = 252 # 一年的交易日
initial_price = 100
# 生成随机游走的股票价格
returns = np.random.normal(0.001, 0.02, days) # 日收益率
prices = initial_price * np.cumprod(1 + returns)
print(f"交易日数: {days}")
print(f"初始价格: ${initial_price:.2f}")
# 价格统计
print(f"\n价格统计:")
print(f"最终价格: ${prices[-1]:.2f}")
print(f"最高价格: ${np.max(prices):.2f}")
print(f"最低价格: ${np.min(prices):.2f}")
print(f"平均价格: ${np.mean(prices):.2f}")
print(f"价格波动率: {np.std(prices):.2f}")
# 收益率分析
print(f"\n收益率分析:")
print(f"平均日收益率: {np.mean(returns)*100:.3f}%")
print(f"收益率标准差: {np.std(returns)*100:.3f}%")
print(f"年化收益率: {np.mean(returns)*252*100:.2f}%")
print(f"年化波动率: {np.std(returns)*np.sqrt(252)*100:.2f}%")
# 风险指标
print(f"\n风险指标:")
# 最大回撤
peak = np.maximum.accumulate(prices)
drawdown = (prices - peak) / peak
max_drawdown = np.min(drawdown)
print(f"最大回撤: {max_drawdown*100:.2f}%")
# VaR (Value at Risk) 95%置信度
var_95 = np.percentile(returns, 5)
print(f"95% VaR: {var_95*100:.3f}%")
7. 性能优化技巧
7.1 向量化vs循环
# 17. 性能优化对比
print("\n⚡ 性能优化对比:")
# 创建大数组
size = 1000000
a = np.random.random(size)
b = np.random.random(size)
# 方法1: Python循环
def python_multiply(x, y):
result = []
for i in range(len(x)):
result.append(x[i] * y[i])
return result
# 方法2: NumPy向量化
def numpy_multiply(x, y):
return x * y
# 方法3: 使用ufunc
def ufunc_multiply(x, y):
return np.multiply(x, y)
# 性能测试
import time
# 测试小数组以避免内存问题
test_size = 10000
test_a = a[:test_size]
test_b = b[:test_size]
# Python循环
start = time.time()
result1 = python_multiply(test_a, test_b)
python_time = time.time() - start
# NumPy向量化
start = time.time()
result2 = numpy_multiply(a, b)
numpy_time = time.time() - start
# ufunc
start = time.time()
result3 = ufunc_multiply(a, b)
ufunc_time = time.time() - start
print(f"Python循环时间 ({test_size}元素): {python_time:.6f}秒")
print(f"NumPy向量化时间 ({size}元素): {numpy_time:.6f}秒")
print(f"ufunc时间 ({size}元素): {ufunc_time:.6f}秒")
print(f"NumPy相对Python提升: {python_time/numpy_time*test_size/size:.0f}倍")
7.2 内存优化
# 18. 内存优化技巧
print("\n💾 内存优化技巧:")
# 就地操作
arr = np.random.random(1000000)
print(f"原始数组内存地址: {id(arr)}")
# 创建新数组的操作
arr_new = arr * 2
print(f"新数组内存地址: {id(arr_new)}")
# 就地操作
arr *= 2
print(f"就地操作后内存地址: {id(arr)}")
# 使用out参数
a = np.random.random(1000)
b = np.random.random(1000)
result = np.empty_like(a) # 预分配结果数组
# 使用out参数避免创建临时数组
np.add(a, b, out=result)
print(f"使用out参数避免临时数组创建")
# 数据类型优化
print(f"\n数据类型优化:")
large_int_array = np.random.randint(0, 255, 1000000, dtype=np.int64)
small_int_array = large_int_array.astype(np.uint8)
print(f"int64数组大小: {large_int_array.nbytes / 1024 / 1024:.2f} MB")
print(f"uint8数组大小: {small_int_array.nbytes / 1024 / 1024:.2f} MB")
print(f"内存节省: {(1 - small_int_array.nbytes/large_int_array.nbytes)*100:.1f}%")
8. 本章小结
8.1 核心知识点
通用函数(ufunc)
- 向量化运算的基础
- 支持广播和类型转换
- 比Python循环快数十倍
数学运算
- 基本算术运算
- 三角函数和反三角函数
- 指数、对数和双曲函数
- 舍入和取整函数
统计函数
- 中心趋势:均值、中位数
- 离散程度:标准差、方差
- 分位数和百分位数
- 相关性和协方差分析
比较和逻辑运算
- 逐元素比较
- 逻辑运算符
- 条件选择和集合运算
8.2 最佳实践
- 🚀 优先使用向量化: 避免Python循环,使用NumPy的向量化操作
- 💾 注意内存使用: 使用合适的数据类型,利用就地操作
- 📊 选择合适的轴: 理解axis参数在多维数组中的作用
- 🎯 利用广播: 充分利用NumPy的广播机制简化代码
8.3 常见陷阱
- ❌ 混淆运算符:
*是逐元素乘法,@是矩阵乘法 - ❌ 忽略数据类型: 整数除法可能导致精度丢失
- ❌ 不必要的复制: 频繁创建新数组影响性能
- ❌ 轴参数错误: 搞错axis参数导致计算结果错误
8.4 下一步学习
- 📚 学习数组形状操作和广播机制
- 🔧 掌握线性代数运算
- 📁 了解文件IO操作
- ⚡ 深入性能优化技巧
9. 练习题
9.1 基础练习
统计分析
- 生成1000个正态分布随机数
- 计算基本统计量
- 找出异常值(超过3个标准差)
数学运算
- 创建角度数组,计算对应的三角函数值
- 绘制sin、cos、tan函数图像
- 验证三角恒等式
数组比较
- 创建两个随机数组
- 找出相同位置的元素
- 统计满足条件的元素个数
9.2 进阶练习
金融计算
- 计算股票的移动平均线
- 计算收益率的滚动标准差
- 实现简单的技术指标
科学计算
- 实现数值积分算法
- 计算函数的数值导数
- 求解非线性方程
数据分析
- 分析多变量数据的相关性
- 实现数据标准化
- 检测和处理异常值
9.3 挑战练习
性能优化
- 比较不同算法的性能
- 优化内存使用
- 实现并行计算
算法实现
- 用NumPy实现机器学习算法
- 实现图像处理算法
- 开发数值分析工具
恭喜您完成第4章的学习! 🎉
您已经掌握了NumPy的数学运算和统计函数,这为后续的数据分析和科学计算奠定了坚实基础。在下一章中,我们将学习数组形状操作和广播机制。