📚 本章概述

本章将深入探讨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 核心知识点

  1. 通用函数(ufunc)

    • 向量化运算的基础
    • 支持广播和类型转换
    • 比Python循环快数十倍
  2. 数学运算

    • 基本算术运算
    • 三角函数和反三角函数
    • 指数、对数和双曲函数
    • 舍入和取整函数
  3. 统计函数

    • 中心趋势:均值、中位数
    • 离散程度:标准差、方差
    • 分位数和百分位数
    • 相关性和协方差分析
  4. 比较和逻辑运算

    • 逐元素比较
    • 逻辑运算符
    • 条件选择和集合运算

8.2 最佳实践

  • 🚀 优先使用向量化: 避免Python循环,使用NumPy的向量化操作
  • 💾 注意内存使用: 使用合适的数据类型,利用就地操作
  • 📊 选择合适的轴: 理解axis参数在多维数组中的作用
  • 🎯 利用广播: 充分利用NumPy的广播机制简化代码

8.3 常见陷阱

  • 混淆运算符: *是逐元素乘法,@是矩阵乘法
  • 忽略数据类型: 整数除法可能导致精度丢失
  • 不必要的复制: 频繁创建新数组影响性能
  • 轴参数错误: 搞错axis参数导致计算结果错误

8.4 下一步学习

  • 📚 学习数组形状操作和广播机制
  • 🔧 掌握线性代数运算
  • 📁 了解文件IO操作
  • ⚡ 深入性能优化技巧

9. 练习题

9.1 基础练习

  1. 统计分析

    • 生成1000个正态分布随机数
    • 计算基本统计量
    • 找出异常值(超过3个标准差)
  2. 数学运算

    • 创建角度数组,计算对应的三角函数值
    • 绘制sin、cos、tan函数图像
    • 验证三角恒等式
  3. 数组比较

    • 创建两个随机数组
    • 找出相同位置的元素
    • 统计满足条件的元素个数

9.2 进阶练习

  1. 金融计算

    • 计算股票的移动平均线
    • 计算收益率的滚动标准差
    • 实现简单的技术指标
  2. 科学计算

    • 实现数值积分算法
    • 计算函数的数值导数
    • 求解非线性方程
  3. 数据分析

    • 分析多变量数据的相关性
    • 实现数据标准化
    • 检测和处理异常值

9.3 挑战练习

  1. 性能优化

    • 比较不同算法的性能
    • 优化内存使用
    • 实现并行计算
  2. 算法实现

    • 用NumPy实现机器学习算法
    • 实现图像处理算法
    • 开发数值分析工具

恭喜您完成第4章的学习! 🎉

您已经掌握了NumPy的数学运算和统计函数,这为后续的数据分析和科学计算奠定了坚实基础。在下一章中,我们将学习数组形状操作和广播机制。

👉 继续学习第5章:数组形状操作与广播