学习目标:掌握NumPy数组操作和向量化编程思维,建立高效数值计算的基础能力,为机器学习铺路
(纯小白建议手敲,都是这么来的,不然就是看得快忘得快)
从Python原生列表到NumPy数组,这不仅仅是工具的升级,更是编程思维的根本转变。我们将从"逐个处理"的标量思维,转向"整体操作"的向量化思维,这是数据科学和机器学习领域的基础能力。
5.1 NumPy数组:从列表到矩阵的思维转换
> 为什么需要NumPy
想象一下,你要对一万个数字进行数学运算。用Python列表,你需要写循环逐个处理;而用NumPy,一行代码就能完成所有运算。这种差异不仅体现在代码简洁性上,更重要的是性能差异可能达到数十倍甚至上百倍。
NumPy(Numerical Python) 是Python科学计算的基础库,它提供了高性能的多维数组对象和相关工具。NumPy数组在内存中以连续方式存储,并且运算由优化的C代码执行,这使得它比Python列表快得多。
python
import numpy as np
import time
# 性能对比演示
def performance_comparison():
# 创建大型数据集
size = 1000000
python_list = list(range(size))
numpy_array = np.arange(size)
print("=== Python列表 vs NumPy数组性能对比 ===")
print(f"数据规模:{size:,} 个元素\n")
# Python列表操作
start_time = time.time()
python_result = [x * 2 + 1 for x in python_list]
python_time = time.time() - start_time
# NumPy数组操作
start_time = time.time()
numpy_result = numpy_array * 2 + 1
numpy_time = time.time() - start_time
print(f"Python列表时间:{python_time:.4f}秒")
print(f"NumPy数组时间:{numpy_time:.4f}秒")
print(f"NumPy速度提升:{python_time/numpy_time:.1f}倍")
# 内存使用对比
import sys
list_memory = sys.getsizeof(python_list) + sum(sys.getsizeof(x) for x in python_list[:100])
array_memory = numpy_array.nbytes
print(f"\n内存使用(估算):")
print(f"Python列表:~{list_memory//1000000:.1f}MB")
print(f"NumPy数组:{array_memory/1000000:.1f}MB")
performance_comparison()

> NumPy数组的创建方法
NumPy数组的创建方式多样,每种方法都有其特定的应用场景:
python
import numpy as np
print("=== NumPy数组创建方法大全 ===\n")
# 1. 从Python列表创建
print("1. 从列表创建数组")
list_1d = [1, 2, 3, 4, 5]
array_1d = np.array(list_1d)
print(f"一维数组:{array_1d}")
print(f"数据类型:{array_1d.dtype}")
print(f"形状:{array_1d.shape}")
print(f"维度:{array_1d.ndim}")
# 二维数组(矩阵)
list_2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
array_2d = np.array(list_2d)
print(f"\n二维数组:\n{array_2d}")
print(f"形状:{array_2d.shape}")
print(f"总元素数:{array_2d.size}")
# 2. 使用内置函数创建
print("\n2. 使用内置函数创建")
# 全零数组
zeros_array = np.zeros((3, 4))
print(f"全零数组:\n{zeros_array}")
# 全一数组
ones_array = np.ones((2, 3, 2))
print(f"全一数组形状:{ones_array.shape}")
# 单位矩阵
identity_matrix = np.eye(4)
print(f"单位矩阵:\n{identity_matrix}")
# 等差数列
range_array = np.arange(0, 20, 2) # 起始、结束、步长
print(f"等差数列:{range_array}")
# 线性空间
linspace_array = np.linspace(0, 10, 5) # 起始、结束、元素个数
print(f"线性空间:{linspace_array}")
# 随机数组
np.random.seed(42) # 设置随机种子确保结果可重现
random_array = np.random.random((2, 3))
print(f"随机数组:\n{random_array}")
# 正态分布随机数
normal_array = np.random.normal(0, 1, (2, 4)) # 均值、标准差、形状
print(f"正态分布随机数:\n{normal_array}")
# 3. 指定数据类型创建
print("\n3. 指定数据类型创建")
int_array = np.array([1, 2, 3], dtype=np.int32)
float_array = np.array([1, 2, 3], dtype=np.float64)
bool_array = np.array([1, 0, 1], dtype=bool)
print(f"整数数组:{int_array},类型:{int_array.dtype}")
print(f"浮点数组:{float_array},类型:{float_array.dtype}")
print(f"布尔数组:{bool_array},类型:{bool_array.dtype}")
> 数组属性的深入理解
理解NumPy数组的属性是掌握其使用的关键:
python
# 创建示例数组用于属性分析
sample_array = np.random.randint(1, 100, size=(3, 4, 5))
print("=== NumPy数组属性详解 ===")
print(f"数组内容:\n{sample_array}\n")
# 基本属性
print("基本属性:")
print(f"形状 (shape):{sample_array.shape}")
print(f"维度数 (ndim):{sample_array.ndim}")
print(f"总元素数 (size):{sample_array.size}")
print(f"数据类型 (dtype):{sample_array.dtype}")
# 内存相关属性
print(f"\n内存相关:")
print(f"每个元素字节数 (itemsize):{sample_array.itemsize}")
print(f"总内存占用 (nbytes):{sample_array.nbytes} 字节")
print(f"内存布局 (flags.c_contiguous):{sample_array.flags.c_contiguous}")
# 形状操作
print(f"\n形状操作演示:")
original_shape = sample_array.shape
reshaped = sample_array.reshape(4, 15) # 重塑为4×15
print(f"原形状:{original_shape}")
print(f"重塑后:{reshaped.shape}")
# 展平操作
flattened = sample_array.flatten()
print(f"展平后:{flattened.shape}")
print(f"展平前后元素总数:{sample_array.size} → {flattened.size}")
# 转置操作
transposed = sample_array.transpose()
print(f"转置前:{sample_array.shape}")
print(f"转置后:{transposed.shape}")
5.2 数组索引与切片:精确访问数据的艺术
> 一维数组索引
NumPy数组的索引既保持了Python列表的基本语法,又提供了更强大的功能:
python
# 一维数组索引示例
arr_1d = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
print("=== 一维数组索引操作 ===")
print(f"原数组:{arr_1d}")
print(f"数组长度:{len(arr_1d)}")
# 基本索引
print(f"\n基本索引:")
print(f"第一个元素 arr[0]:{arr_1d[0]}")
print(f"最后一个元素 arr[-1]:{arr_1d[-1]}")
print(f"倒数第二个 arr[-2]:{arr_1d[-2]}")
# 切片操作
print(f"\n切片操作:")
print(f"前5个元素 arr[:5]:{arr_1d[:5]}")
print(f"后5个元素 arr[5:]:{arr_1d[5:]}")
print(f"中间元素 arr[2:8]:{arr_1d[2:8]}")
print(f"每隔2个 arr[::2]:{arr_1d[::2]}")
print(f"反向切片 arr[::-1]:{arr_1d[::-1]}")
# 布尔索引(强大的筛选功能)
print(f"\n布尔索引:")
condition = arr_1d > 50
print(f"大于50的条件:{condition}")
print(f"大于50的元素:{arr_1d[condition]}")
print(f"大于50且小于80:{arr_1d[(arr_1d > 50) & (arr_1d < 80)]}")
# 花式索引
print(f"\n花式索引:")
indices = [0, 2, 4, 6]
print(f"指定位置元素:{arr_1d[indices]}")
# 索引数组
index_array = np.array([1, 3, 5, 7, 9])
print(f"索引数组访问:{arr_1d[index_array]}")
> 多维数组索引
多维数组的索引是NumPy的核心功能,掌握它对于处理矩阵和张量至关重要:
python
# 创建多维数组示例
arr_2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]])
arr_3d = np.random.randint(1, 50, size=(2, 3, 4))
print("=== 多维数组索引操作 ===")
print(f"二维数组:\n{arr_2d}")
print(f"三维数组形状:{arr_3d.shape}")
# 二维数组索引
print(f"\n二维数组索引:")
print(f"元素 [1,2]:{arr_2d[1, 2]}")
print(f"第2行:{arr_2d[1]}") # 等同于 arr_2d[1, :]
print(f"第3列:{arr_2d[:, 2]}")
print(f"左上角2×2:\n{arr_2d[:2, :2]}")
print(f"右下角2×2:\n{arr_2d[2:, 2:]}")
# 步长切片
print(f"\n步长切片:")
print(f"每隔一行:\n{arr_2d[::2]}")
print(f"每隔一列:\n{arr_2d[:, ::2]}")
print(f"行列都间隔:\n{arr_2d[::2, ::2]}")
# 布尔索引在二维数组中的应用
print(f"\n二维数组布尔索引:")
print(f"大于8的元素:{arr_2d[arr_2d > 8]}")
print(f"大于8的位置:")
positions = np.where(arr_2d > 8)
print(f"行索引:{positions[0]}")
print(f"列索引:{positions[1]}")
# 条件赋值
arr_copy = arr_2d.copy()
arr_copy[arr_copy > 8] = 0 # 将大于8的元素设为0
print(f"条件赋值后:\n{arr_copy}")
# 三维数组索引示例
print(f"\n三维数组索引:")
print(f"第一个2D切片:\n{arr_3d[0]}")
print(f"所有第一行:\n{arr_3d[:, 0, :]}")
print(f"所有第一列:\n{arr_3d[:, :, 0]}")
print(f"特定元素 [0,1,2]:{arr_3d[0, 1, 2]}")
> 高级索引技巧
NumPy提供了一些高级索引技巧,让数据操作更加灵活:
python
# 高级索引技巧演示
data = np.random.randint(1, 100, size=(5, 6))
print("=== 高级索引技巧 ===")
print(f"原始数据:\n{data}")
# 1. 使用索引数组进行行选择
print(f"\n1. 索引数组选择:")
row_indices = [0, 2, 4] # 选择第1、3、5行
selected_rows = data[row_indices]
print(f"选择的行:\n{selected_rows}")
# 2. 同时选择特定行和列
print(f"\n2. 行列同时选择:")
row_idx = [1, 3]
col_idx = [0, 2, 4]
# 使用np.ix_创建网格索引
selected_subset = data[np.ix_(row_idx, col_idx)]
print(f"选择结果:\n{selected_subset}")
# 3. 掩码数组(mask array)
print(f"\n3. 掩码数组应用:")
# 创建掩码:偶数位置为True
mask = (data % 2 == 0)
print(f"偶数掩码(部分):\n{mask[:3, :3]}")
print(f"所有偶数:{data[mask][:10]}...") # 显示前10个
# 4. 条件替换
print(f"\n4. 条件替换:")
modified_data = data.copy()
modified_data[modified_data < 30] = -1 # 小于30的设为-1
modified_data[modified_data >= 70] = 99 # 大于等于70的设为99
print(f"替换后(部分):\n{modified_data[:3, :3]}")
# 5. 使用where函数进行条件选择
print(f"\n5. where函数应用:")
result = np.where(data > 50, data, 0) # 大于50保持原值,否则为0
print(f"条件选择结果(部分):\n{result[:3, :3]}")
# 6. argmax和argmin的使用
print(f"\n6. 最值索引:")
print(f"最大值:{data.max()},位置:{data.argmax()}")
print(f"最小值:{data.min()},位置:{data.argmin()}")
# 转换为2D坐标
max_pos_2d = np.unravel_index(data.argmax(), data.shape)
min_pos_2d = np.unravel_index(data.argmin(), data.shape)
print(f"最大值2D位置:{max_pos_2d}")
print(f"最小值2D位置:{min_pos_2d}")
5.3 向量化运算:告别循环的高效计算
> 什么是向量化运算
向量化运算(Vectorization) 是NumPy的核心优势,它允许我们对整个数组进行运算,而不需要编写显式的循环。这种运算方式不仅代码更简洁,执行效率也大大提高。
python
import numpy as np
import time
# 向量化 vs 循环的对比示例
def vectorization_demo():
# 创建大型数组进行测试
size = 1000000
arr1 = np.random.random(size)
arr2 = np.random.random(size)
print("=== 向量化运算 vs 传统循环 ===")
print(f"数组大小:{size:,} 元素\n")
# 传统Python循环方法
start_time = time.time()
result_loop = []
for i in range(len(arr1)):
result_loop.append(arr1[i] * arr2[i] + np.sin(arr1[i]))
loop_time = time.time() - start_time
# NumPy向量化方法
start_time = time.time()
result_vectorized = arr1 * arr2 + np.sin(arr1)
vectorized_time = time.time() - start_time
print(f"传统循环时间:{loop_time:.4f}秒")
print(f"向量化时间:{vectorized_time:.4f}秒")
print(f"性能提升:{loop_time/vectorized_time:.1f}倍")
# 验证结果一致性
print(f"结果一致性检查:{np.allclose(result_loop, result_vectorized)}")
return arr1, arr2
# 运行演示
test_arr1, test_arr2 = vectorization_demo()

> 基本数学运算
NumPy支持所有基本数学运算的向量化:
python
# 基本向量化运算演示
arr = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
print("=== 基本向量化运算 ===")
print(f"原数组:\n{arr}")
# 标量运算(广播)
print(f"\n标量运算:")
print(f"每个元素加5:\n{arr + 5}")
print(f"每个元素乘以2:\n{arr * 2}")
print(f"每个元素平方:\n{arr ** 2}")
print(f"每个元素开方:\n{np.sqrt(arr)}")
# 数组间运算
arr2 = np.array([[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]])
print(f"\n数组间运算:")
print(f"数组2:\n{arr2}")
print(f"相加:\n{arr + arr2}")
print(f"相乘:\n{arr * arr2}")
print(f"除法:\n{arr / arr2}")
# 比较运算
print(f"\n比较运算:")
print(f"大于6:\n{arr > 6}")
print(f"等于偶数:\n{arr % 2 == 0}")
# 数学函数
print(f"\n数学函数:")
float_arr = arr.astype(float)
print(f"正弦值:\n{np.sin(float_arr)[:2]}") # 只显示前两行
print(f"自然对数:\n{np.log(float_arr)[:2]}")
print(f"指数:\n{np.exp(float_arr/10)[:2]}") # 除以10避免数值过大
> 统计运算
NumPy提供了丰富的统计函数,都支持向量化操作:
python
# 统计运算演示
data = np.random.normal(50, 15, size=(4, 6)) # 正态分布数据
data = np.round(data, 2) # 保留两位小数
print("=== 统计运算 ===")
print(f"数据样本:\n{data}")
# 基本统计
print(f"\n基本统计:")
print(f"平均值:{np.mean(data):.2f}")
print(f"中位数:{np.median(data):.2f}")
print(f"标准差:{np.std(data):.2f}")
print(f"方差:{np.var(data):.2f}")
print(f"最大值:{np.max(data):.2f}")
print(f"最小值:{np.min(data):.2f}")
print(f"范围:{np.ptp(data):.2f}") # peak to peak
# 按轴统计
print(f"\n按轴统计:")
print(f"每行平均值:{np.mean(data, axis=1)}") # axis=1表示沿行计算
print(f"每列平均值:{np.mean(data, axis=0)}") # axis=0表示沿列计算
print(f"每行最大值:{np.max(data, axis=1)}")
print(f"每列最小值:{np.min(data, axis=0)}")
# 累积统计
print(f"\n累积统计:")
print(f"累积和(前5个):{np.cumsum(data.flatten())[:5]}")
print(f"累积乘积(前5个):{np.cumprod(data.flatten())[:5]}")
# 分位数
print(f"\n分位数:")
percentiles = [25, 50, 75, 90, 95]
for p in percentiles:
print(f"{p}%分位数:{np.percentile(data, p):.2f}")
# 相关性分析
print(f"\n相关性分析:")
if data.shape[1] >= 2: # 确保至少有两列
correlation = np.corrcoef(data[:, 0], data[:, 1])[0, 1]
print(f"第1列和第2列相关系数:{correlation:.3f}")
5.4 广播机制:不同形状数组的运算规则
> 广播的概念和规则
广播(Broadcasting) 是NumPy中一个强大的功能,它允许不同形状的数组进行运算,无需手动调整数组形状。理解广播规则对于高效使用NumPy至关重要。
广播的核心规则可以分为4个步骤,逐一解释:
规则1:从右往左对齐维度
NumPy总是从最后一个维度开始比较,而不是从第一个。
python
# 例子:
数组A形状: (2, 3, 4)
数组B形状: (3, 4)
# 对齐过程:
A: 2 3 4
B: 3 4 ← 从右边开始对齐
规则2:维度兼容性检查
对齐后,每个位置上的维度必须满足以下条件之一:
- 相等
- 其中一个是1
- 其中一个缺失(视为1)
python
# 可以广播的例子:
A: (2, 3, 4)
B: (3, 4) # 缺失的第一个维度视为1,即(1, 3, 4)
检查过程:
位置1: 2 vs 1 ✓ (其中一个是1)
位置2: 3 vs 3 ✓ (相等)
位置3: 4 vs 4 ✓ (相等)
# 不能广播的例子:
A: (2, 3, 4)
B: (2, 4) # 等价于(1, 2, 4)
检查过程:
位置1: 2 vs 1 ✓ (其中一个是1)
位置2: 3 vs 2 ✗ (不相等,且都不是1)
位置3: 4 vs 4 ✓ (相等)
规则3:缺失维度补1
较短的形状在左边补1,直到两个形状长度相同。
python
原始形状:
A: (2, 3, 4)
B: (4,)
补齐后:
A: (2, 3, 4)
B: (1, 1, 4) # 在左边补两个1
规则4:确定结果形状
结果的每个维度取两个数组中较大的那个。
python
A: (2, 3, 4)
B: (1, 1, 4)
结果形状:
位置1: max(2, 1) = 2
位置2: max(3, 1) = 3
位置3: max(4, 4) = 4
最终结果: (2, 3, 4)
完整的判断流程
让我用一个具体例子演示完整流程:
python
# 判断 (3, 4, 5) 和 (4, 1) 能否广播
步骤1:从右往左对齐
A: 3 4 5
B: 4 1
步骤2:补齐较短的形状
A: (3, 4, 5)
B: (1, 4, 1) # 在左边补一个1
步骤3:逐位置检查兼容性
位置1: 3 vs 1 ✓ (其中一个是1)
位置2: 4 vs 4 ✓ (相等)
位置3: 5 vs 1 ✓ (其中一个是1)
步骤4:确定结果形状
结果: (max(3,1), max(4,4), max(5,1)) = (3, 4, 5)
结论:可以广播,结果形状是(3, 4, 5)
常见的错误理解
错误1:以为是从左往右比较
python
# 这样想是错的:
A: (3, 4)
B: (2, 4)
认为第一个维度3≠2所以不能广播
# 正确的思路:
A: (3, 4)
B: (2, 4)
最后一个维度4==4,但倒数第二个维度3≠2且都不是1,所以不能广播
错误2:以为任何时候都能广播
python
# 这些不能广播:
(3, 4) + (2, 4) # 3≠2,且都不是1
(3, 4) + (3, 2) # 4≠2,且都不是1
这里是引用
记忆口诀
"右对齐,查兼容,补1左,取较大"
- 右对齐:从右边开始比较维度
- 查兼容:相等或有1才兼容
- 补1左:较短的形状左边补1
- 取较大:结果维度取较大值
广播机制的目的有三个:
-
便利性
让你不用手动把小数组"扩展"成大数组就能直接运算。比如你想给每个学生的成绩都加10分,不用先创建一个和成绩数组一样大小、全是10的数组,直接加就行。
-
内存效率
NumPy并不会真的创建那个"扩展后的大数组",它只是在运算时假装有这个数组。这样节省了大量内存空间。
-
计算性能
底层的C代码可以直接优化这种运算,比你先扩展数组再运算要快很多。
关于结果
广播完全不会影响最终的数学结果。
如果你手动把小数组复制扩展成大数组,然后运算,得到的结果和广播的结果是完全一样的。广播只是让这个过程变得更简单、更快、更省内存。
本质上,广播就是NumPy的一个"聪明的偷懒方式"------既让程序员写代码更轻松,又让计算机跑得更高效,还保证了结果的正确性。
python
# 广播机制演示
print("=== NumPy广播机制详解 ===")
# 基本广播示例
print("1. 标量与数组的广播:")
arr = np.array([[1, 2, 3],
[4, 5, 6]])
scalar = 10
result = arr + scalar
print(f"数组形状:{arr.shape}")
print(f"数组:\n{arr}")
print(f"标量:{scalar}")
print(f"广播结果:\n{result}")
print(f"\n2. 一维数组与二维数组的广播:")
arr_2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
arr_1d = np.array([10, 20, 30])
print(f"二维数组形状:{arr_2d.shape}")
print(f"一维数组形状:{arr_1d.shape}")
print(f"二维数组:\n{arr_2d}")
print(f"一维数组:{arr_1d}")
# 广播运算
result_add = arr_2d + arr_1d
result_mult = arr_2d * arr_1d
print(f"相加结果:\n{result_add}")
print(f"相乘结果:\n{result_mult}")
# 列向量广播
print(f"\n3. 列向量广播:")
col_vector = np.array([[100],
[200],
[300]])
print(f"列向量形状:{col_vector.shape}")
print(f"列向量:\n{col_vector}")
result_col = arr_2d + col_vector
print(f"与二维数组相加:\n{result_col}")
> 广播规则详解
理解广播的具体规则能帮助我们预测运算结果和避免错误:
python
def explain_broadcasting(arr1_shape, arr2_shape):
"""解释两个形状是否可以广播"""
print(f"\n=== 广播规则分析 ===")
print(f"数组1形状:{arr1_shape}")
print(f"数组2形状:{arr2_shape}")
# 从右往左比较维度
max_ndim = max(len(arr1_shape), len(arr2_shape))
# 补齐较短的形状(在左边填1)
shape1_padded = [1] * (max_ndim - len(arr1_shape)) + list(arr1_shape)
shape2_padded = [1] * (max_ndim - len(arr2_shape)) + list(arr2_shape)
print(f"补齐后形状1:{shape1_padded}")
print(f"补齐后形状2:{shape2_padded}")
# 检查每个维度
result_shape = []
can_broadcast = True
for i in range(max_ndim):
dim1, dim2 = shape1_padded[i], shape2_padded[i]
if dim1 == dim2:
result_shape.append(dim1)
print(f"维度{i}:{dim1} == {dim2} ✓")
elif dim1 == 1:
result_shape.append(dim2)
print(f"维度{i}:{dim1} → {dim2} ✓ (广播)")
elif dim2 == 1:
result_shape.append(dim1)
print(f"维度{i}:{dim2} → {dim1} ✓ (广播)")
else:
print(f"维度{i}:{dim1} ≠ {dim2} ✗ (不兼容)")
can_broadcast = False
break
if can_broadcast:
print(f"结果形状:{result_shape}")
print("✓ 可以进行广播运算")
else:
print("✗ 无法进行广播运算")
return can_broadcast, result_shape if can_broadcast else None
# 测试各种广播情况
test_cases = [
((3, 4), (4,)), # 标准广播
((3, 4), (3, 1)), # 列广播
((3, 4), (1, 4)), # 行广播
((3, 4), (3, 4)), # 相同形状
((3, 4), (2, 4)), # 不兼容
((2, 3, 4), (3, 4)), # 三维与二维
((2, 1, 4), (3, 1)), # 复杂广播
]
for shape1, shape2 in test_cases:
explain_broadcasting(shape1, shape2)
print("-" * 50)
> 广播的实际应用
广播在实际应用中非常有用,特别是在数据预处理和特征工程中:
python
# 广播的实际应用示例
print("=== 广播机制实际应用 ===")
# 1. 数据标准化(Z-score标准化)
print("1. 数据标准化:")
# 模拟学生成绩数据(行:学生,列:科目)
scores = np.random.randint(60, 100, size=(5, 4))
subjects = ['数学', '英语', '物理', '化学']
print("原始成绩:")
for i, subject in enumerate(subjects):
print(f"{subject}:{scores[:, i]}")
# 计算每科平均分和标准差
means = np.mean(scores, axis=0) # 每列(科目)的平均分
stds = np.std(scores, axis=0) # 每列(科目)的标准差
print(f"\n各科平均分:{means}")
print(f"各科标准差:{stds}")
# 标准化(使用广播)
standardized_scores = (scores - means) / stds
print(f"\n标准化后成绩:\n{standardized_scores}")
# 验证标准化结果
print(f"标准化后平均值:{np.mean(standardized_scores, axis=0)}")
print(f"标准化后标准差:{np.std(standardized_scores, axis=0)}")
# 2. 图像处理中的广播
print(f"\n2. 图像处理应用:")
# 模拟RGB图像(高×宽×通道)
image = np.random.randint(0, 256, size=(100, 100, 3), dtype=np.uint8)
print(f"图像形状:{image.shape}")
# 调整亮度(每个通道加上不同的值)
brightness_adjustment = np.array([20, -10, 15]) # R, G, B通道的调整值
adjusted_image = image + brightness_adjustment
adjusted_image = np.clip(adjusted_image, 0, 255) # 确保像素值在有效范围内
print(f"亮度调整值:{brightness_adjustment}")
print(f"调整前像素值样本:{image[0, 0]}")
print(f"调整后像素值样本:{adjusted_image[0, 0]}")
# 3. 距离计算
print(f"\n3. 批量距离计算:")
# 计算多个点到参考点的距离
points = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
reference_point = np.array([0, 0])
print(f"点坐标:\n{points}")
print(f"参考点:{reference_point}")
# 使用广播计算欧几里得距离
diff = points - reference_point # 广播:(4,2) - (2,) → (4,2)
distances = np.sqrt(np.sum(diff**2, axis=1))
print(f"距离:{distances}")
# 4. 网格生成
print(f"\n4. 坐标网格生成:")
x = np.array([1, 2, 3])
y = np.array([10, 20])
# 创建坐标网格
X, Y = np.meshgrid(x, y)
print(f"X坐标网格:\n{X}")
print(f"Y坐标网格:\n{Y}")
# 使用广播计算函数值
x_reshaped = x.reshape(1, -1) # (1, 3)
y_reshaped = y.reshape(-1, 1) # (2, 1)
Z = x_reshaped**2 + y_reshaped**2 # 广播计算
print(f"函数值 z = x² + y²:\n{Z}")
5.5 线性代数运算:矩阵运算的强大工具
> 矩阵基础运算
线性代数是机器学习和科学计算的基础,NumPy提供了完整的线性代数功能:
python
import numpy as np
print("=== 线性代数基础运算 ===")
# 创建矩阵
A = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
B = np.array([[9, 8, 7],
[6, 5, 4],
[3, 2, 1]])
vector = np.array([1, 2, 3])
print(f"矩阵A:\n{A}")
print(f"矩阵B:\n{B}")
print(f"向量:{vector}")
# 矩阵乘法
print(f"\n=== 矩阵乘法 ===")
# 注意:* 是逐元素乘法,@ 或 np.dot 是矩阵乘法
print(f"逐元素乘法 A * B:\n{A * B}")
print(f"矩阵乘法 A @ B:\n{A @ B}")
print(f"矩阵乘法 np.dot(A, B):\n{np.dot(A, B)}")
# 矩阵与向量相乘
print(f"矩阵乘以向量 A @ vector:{A @ vector}")
# 转置
print(f"\n=== 转置运算 ===")
print(f"A的转置:\n{A.T}")
print(f"A转置的另一种写法:\n{np.transpose(A)}")
# 矩阵的迹(对角线元素之和)
print(f"\n=== 矩阵的迹 ===")
print(f"矩阵A的迹:{np.trace(A)}")
# 行列式
print(f"\n=== 行列式 ===")
det_A = np.linalg.det(A)
print(f"矩阵A的行列式:{det_A:.6f}")
# 创建一个可逆矩阵用于演示
C = np.array([[2, 1, 0],
[1, 2, 1],
[0, 1, 2]], dtype=float)
det_C = np.linalg.det(C)
print(f"矩阵C:\n{C}")
print(f"矩阵C的行列式:{det_C:.6f}")
> 矩阵分解和特殊运算
NumPy提供了多种矩阵分解方法,这些在机器学习中非常重要:
python
# 矩阵分解和特殊运算
print("=== 矩阵分解和特殊运算 ===")
# 创建对称正定矩阵用于演示
np.random.seed(42)
random_matrix = np.random.randn(4, 4)
symmetric_matrix = random_matrix @ random_matrix.T # 确保正定
print(f"对称矩阵:\n{symmetric_matrix}")
# 1. 特征值分解
print(f"\n1. 特征值分解:")
eigenvalues, eigenvectors = np.linalg.eig(symmetric_matrix)
print(f"特征值:{eigenvalues}")
print(f"特征向量形状:{eigenvectors.shape}")
print(f"第一个特征向量:{eigenvectors[:, 0]}")
# 验证特征值和特征向量
print(f"验证 Av = λv(第一组):")
Av = symmetric_matrix @ eigenvectors[:, 0]
lv = eigenvalues[0] * eigenvectors[:, 0]
print(f"Av = {Av}")
print(f"λv = {lv}")
print(f"相等性检查:{np.allclose(Av, lv)}")
# 2. 奇异值分解(SVD)
print(f"\n2. 奇异值分解:")
U, s, Vt = np.linalg.svd(symmetric_matrix)
print(f"U形状:{U.shape}")
print(f"奇异值:{s}")
print(f"Vt形状:{Vt.shape}")
# 重构矩阵验证SVD
S = np.diag(s)
reconstructed = U @ S @ Vt
print(f"重构矩阵与原矩阵相等:{np.allclose(symmetric_matrix, reconstructed)}")
# 3. QR分解
print(f"\n3. QR分解:")
Q, R = np.linalg.qr(symmetric_matrix)
print(f"Q形状:{Q.shape}")
print(f"R形状:{R.shape}")
print(f"Q是正交矩阵:{np.allclose(Q @ Q.T, np.eye(4))}")
print(f"重构检查:{np.allclose(Q @ R, symmetric_matrix)}")
# 4. 逆矩阵和伪逆
print(f"\n4. 逆矩阵运算:")
try:
inv_C = np.linalg.inv(C)
print(f"矩阵C的逆:\n{inv_C}")
print(f"验证 C @ C^(-1) = I:\n{C @ inv_C}")
except np.linalg.LinAlgError:
print("矩阵不可逆")
# 伪逆(对于奇异矩阵)
pinv_A = np.linalg.pinv(A) # A是奇异的(行列式为0)
print(f"矩阵A的伪逆形状:{pinv_A.shape}")
# 5. 范数计算
print(f"\n5. 矩阵范数:")
print(f"Frobenius范数:{np.linalg.norm(A, 'fro'):.4f}")
print(f"2-范数(谱范数):{np.linalg.norm(A, 2):.4f}")
print(f"1-范数:{np.linalg.norm(A, 1):.4f}")
print(f"∞-范数:{np.linalg.norm(A, np.inf):.4f}")
# 向量范数
vector_norm = np.array([3, 4, 5])
print(f"向量{vector_norm}的2-范数:{np.linalg.norm(vector_norm):.4f}")
print(f"向量{vector_norm}的1-范数:{np.linalg.norm(vector_norm, 1):.4f}")
> 解线性方程组
NumPy可以高效地解决线性方程组问题:
python
# 解线性方程组
print("=== 解线性方程组 ===")
# 方程组:2x + y = 5
# x + 2y = 4
A_eq = np.array([[2, 1],
[1, 2]], dtype=float)
b_eq = np.array([5, 4], dtype=float)
print(f"系数矩阵A:\n{A_eq}")
print(f"常数向量b:{b_eq}")
# 求解
solution = np.linalg.solve(A_eq, b_eq)
print(f"解:x = {solution[0]:.2f}, y = {solution[1]:.2f}")
# 验证解
verification = A_eq @ solution
print(f"验证 Ax = {verification}")
print(f"目标 b = {b_eq}")
print(f"解正确:{np.allclose(verification, b_eq)}")
# 超定方程组(最小二乘解)
print(f"\n=== 超定方程组(最小二乘) ===")
# 创建一个超定系统(方程数 > 未知数个数)
A_over = np.array([[1, 1],
[1, 2],
[1, 3],
[1, 4]], dtype=float)
b_over = np.array([6, 8, 10, 12], dtype=float)
print(f"超定系统A形状:{A_over.shape}")
print(f"b形状:{b_over.shape}")
# 最小二乘解
lstsq_solution = np.linalg.lstsq(A_over, b_over, rcond=None)
x_lstsq = lstsq_solution[0]
residuals = lstsq_solution[1]
print(f"最小二乘解:{x_lstsq}")
print(f"残差平方和:{residuals}")
# 计算拟合优度
fitted_values = A_over @ x_lstsq
r_squared = 1 - np.sum((b_over - fitted_values)**2) / np.sum((b_over - np.mean(b_over))**2)
print(f"R²值:{r_squared:.4f}")
# 欠定方程组(无穷多解)
print(f"\n=== 欠定方程组 ===")
A_under = np.array([[1, 2, 3]], dtype=float) # 1个方程,3个未知数
b_under = np.array([6], dtype=float)
print(f"欠定系统A形状:{A_under.shape}")
print(f"使用伪逆求最小范数解:")
pinv_solution = np.linalg.pinv(A_under) @ b_under
print(f"最小范数解:{pinv_solution}")
print(f"验证:{A_under @ pinv_solution}")
5.6 实战项目:图像处理工具包
现在让我们将所有学到的NumPy知识综合应用到一个实际项目中:构建一个图像处理工具包。
python
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import hsv_to_rgb, rgb_to_hsv
class ImageProcessor:
"""基于NumPy的图像处理工具包"""
def __init__(self):
self.filters = {
'blur': self._gaussian_blur,
'sharpen': self._sharpen,
'edge_detect': self._edge_detection,
'emboss': self._emboss
}
def create_sample_image(self, size=(100, 100), pattern='gradient'):
"""创建示例图像用于测试"""
height, width = size
if pattern == 'gradient':
# 创建渐变图像
x = np.linspace(0, 1, width)
y = np.linspace(0, 1, height)
X, Y = np.meshgrid(x, y)
# RGB三通道渐变
red = X
green = Y
blue = (X + Y) / 2
image = np.stack([red, green, blue], axis=2)
elif pattern == 'checkerboard':
# 创建棋盘图案
x = np.arange(width) // 10
y = np.arange(height) // 10
checkerboard = (x[np.newaxis, :] + y[:, np.newaxis]) % 2
image = np.stack([checkerboard] * 3, axis=2)
elif pattern == 'circle':
# 创建同心圆图案
center_x, center_y = width // 2, height // 2
x = np.arange(width) - center_x
y = np.arange(height) - center_y
X, Y = np.meshgrid(x, y)
distance = np.sqrt(X**2 + Y**2)
circle_pattern = np.sin(distance / 5) * 0.5 + 0.5
image = np.stack([circle_pattern] * 3, axis=2)
else:
# 随机噪声图像
image = np.random.random(size + (3,))
return np.clip(image, 0, 1)
def adjust_brightness(self, image, factor=1.2):
"""调整亮度"""
return np.clip(image * factor, 0, 1)
def adjust_contrast(self, image, factor=1.5):
"""调整对比度"""
mean = np.mean(image)
return np.clip((image - mean) * factor + mean, 0, 1)
def rgb_to_grayscale(self, image):
"""RGB转灰度图"""
# 使用标准权重:0.299*R + 0.587*G + 0.114*B
weights = np.array([0.299, 0.587, 0.114])
return np.dot(image, weights)
def histogram_equalization(self, image):
"""直方图均衡化"""
if len(image.shape) == 3:
# 彩色图像,对每个通道分别处理
result = np.zeros_like(image)
for i in range(3):
result[:, :, i] = self._equalize_channel(image[:, :, i])
return result
else:
# 灰度图像
return self._equalize_channel(image)
def _equalize_channel(self, channel):
"""对单个通道进行直方图均衡化"""
# 将[0,1]范围转换为[0,255]整数
channel_int = (channel * 255).astype(np.uint8)
# 计算直方图
hist, bins = np.histogram(channel_int, 256, range=(0, 256))
# 计算累积分布函数
cdf = hist.cumsum()
cdf_normalized = cdf * 255 / cdf[-1]
# 应用变换
equalized = cdf_normalized[channel_int]
return equalized / 255.0
def rotate_image(self, image, angle_degrees):
"""旋转图像(简单实现)"""
angle_rad = np.radians(angle_degrees)
cos_a, sin_a = np.cos(angle_rad), np.sin(angle_rad)
height, width = image.shape[:2]
center_x, center_y = width // 2, height // 2
# 创建旋转矩阵
rotation_matrix = np.array([[cos_a, -sin_a],
[sin_a, cos_a]])
# 创建坐标网格
x = np.arange(width) - center_x
y = np.arange(height) - center_y
X, Y = np.meshgrid(x, y)
# 原始坐标
coords = np.stack([X.flatten(), Y.flatten()])
# 应用逆旋转变换
inv_rotation_matrix = rotation_matrix.T
new_coords = inv_rotation_matrix @ coords
# 重新整形坐标
new_X = new_coords[0].reshape(height, width) + center_x
new_Y = new_coords[1].reshape(height, width) + center_y
# 双线性插值(简化版本)
new_X = np.clip(new_X, 0, width - 1)
new_Y = np.clip(new_Y, 0, height - 1)
# 使用最近邻插值
new_X_int = np.round(new_X).astype(int)
new_Y_int = np.round(new_Y).astype(int)
if len(image.shape) == 3:
rotated = image[new_Y_int, new_X_int, :]
else:
rotated = image[new_Y_int, new_X_int]
return rotated
def apply_filter(self, image, filter_name, **kwargs):
"""应用滤镜"""
if filter_name in self.filters:
return self.filters[filter_name](image, **kwargs)
else:
raise ValueError(f"未知滤镜:{filter_name}")
def _gaussian_blur(self, image, kernel_size=5, sigma=1.0):
"""高斯模糊滤镜"""
# 创建高斯核
kernel = self._create_gaussian_kernel(kernel_size, sigma)
return self._apply_convolution(image, kernel)
def _sharpen(self, image, strength=1.0):
"""锐化滤镜"""
kernel = np.array([[-1, -1, -1],
[-1, 9, -1],
[-1, -1, -1]]) * strength
kernel[1, 1] = 8 * strength + 1
return self._apply_convolution(image, kernel)
def _edge_detection(self, image):
"""边缘检测滤镜(Sobel算子)"""
sobel_x = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
sobel_y = np.array([[-1, -2, -1],
[ 0, 0, 0],
[ 1, 2, 1]])
if len(image.shape) == 3:
# 对彩色图像先转换为灰度
gray = self.rgb_to_grayscale(image)
else:
gray = image
edge_x = self._apply_convolution(gray, sobel_x)
edge_y = self._apply_convolution(gray, sobel_y)
edges = np.sqrt(edge_x**2 + edge_y**2)
return np.clip(edges, 0, 1)
def _emboss(self, image):
"""浮雕效果滤镜"""
kernel = np.array([[-2, -1, 0],
[-1, 1, 1],
[ 0, 1, 2]])
result = self._apply_convolution(image, kernel)
# 添加灰度偏移以获得更好的浮雕效果
return np.clip(result + 0.5, 0, 1)
def _create_gaussian_kernel(self, size, sigma):
"""创建高斯核"""
center = size // 2
x = np.arange(size) - center
y = np.arange(size) - center
X, Y = np.meshgrid(x, y)
kernel = np.exp(-(X**2 + Y**2) / (2 * sigma**2))
return kernel / np.sum(kernel)
def _apply_convolution(self, image, kernel):
"""应用卷积运算"""
if len(image.shape) == 2:
# 灰度图像
return self._convolve_2d(image, kernel)
else:
# 彩色图像,对每个通道分别卷积
result = np.zeros_like(image)
for i in range(image.shape[2]):
result[:, :, i] = self._convolve_2d(image[:, :, i], kernel)
return result
def _convolve_2d(self, image, kernel):
"""2D卷积运算(简单实现)"""
kh, kw = kernel.shape
pad_h, pad_w = kh // 2, kw // 2
# 填充图像
padded = np.pad(image, ((pad_h, pad_h), (pad_w, pad_w)), mode='edge')
# 输出图像
output = np.zeros_like(image)
# 卷积运算
for i in range(image.shape[0]):
for j in range(image.shape[1]):
output[i, j] = np.sum(padded[i:i+kh, j:j+kw] * kernel)
return output
def analyze_image(self, image):
"""分析图像统计信息"""
stats = {}
if len(image.shape) == 3:
# 彩色图像
stats['type'] = 'color'
stats['shape'] = image.shape
stats['channels'] = image.shape[2]
# 每个通道的统计
for i, channel in enumerate(['Red', 'Green', 'Blue']):
channel_data = image[:, :, i]
stats[f'{channel}_mean'] = np.mean(channel_data)
stats[f'{channel}_std'] = np.std(channel_data)
stats[f'{channel}_min'] = np.min(channel_data)
stats[f'{channel}_max'] = np.max(channel_data)
else:
# 灰度图像
stats['type'] = 'grayscale'
stats['shape'] = image.shape
stats['mean'] = np.mean(image)
stats['std'] = np.std(image)
stats['min'] = np.min(image)
stats['max'] = np.max(image)
# 通用统计
stats['total_pixels'] = image.size // (3 if len(image.shape) == 3 else 1)
return stats
# 演示图像处理工具包
def demonstrate_image_processing():
"""演示图像处理功能"""
print("=== 图像处理工具包演示 ===")
# 创建处理器实例
processor = ImageProcessor()
# 1. 创建测试图像
print("1. 创建测试图像")
original = processor.create_sample_image(size=(64, 64), pattern='gradient')
print(f"图像形状:{original.shape}")
print(f"像素值范围:[{original.min():.3f}, {original.max():.3f}]")
# 2. 图像分析
print("\n2. 图像分析")
stats = processor.analyze_image(original)
print(f"图像类型:{stats['type']}")
print(f"图像形状:{stats['shape']}")
for channel in ['Red', 'Green', 'Blue']:
if f'{channel}_mean' in stats:
print(f"{channel}通道 - 均值:{stats[f'{channel}_mean']:.3f}, "
f"标准差:{stats[f'{channel}_std']:.3f}")
# 3. 基本图像调整
print("\n3. 基本图像调整")
bright_image = processor.adjust_brightness(original, factor=1.3)
contrast_image = processor.adjust_contrast(original, factor=1.5)
gray_image = processor.rgb_to_grayscale(original)
print(f"亮度调整后范围:[{bright_image.min():.3f}, {bright_image.max():.3f}]")
print(f"对比度调整后范围:[{contrast_image.min():.3f}, {contrast_image.max():.3f}]")
print(f"灰度图像形状:{gray_image.shape}")
# 4. 滤镜应用
print("\n4. 滤镜效果")
blurred = processor.apply_filter(original, 'blur', kernel_size=5, sigma=1.0)
sharpened = processor.apply_filter(original, 'sharpen', strength=0.5)
edges = processor.apply_filter(original, 'edge_detect')
embossed = processor.apply_filter(original, 'emboss')
print(f"模糊滤镜应用完成,输出范围:[{blurred.min():.3f}, {blurred.max():.3f}]")
print(f"锐化滤镜应用完成,输出范围:[{sharpened.min():.3f}, {sharpened.max():.3f}]")
print(f"边缘检测完成,输出范围:[{edges.min():.3f}, {edges.max():.3f}]")
print(f"浮雕效果完成,输出范围:[{embossed.min():.3f}, {embossed.max():.3f}]")
# 5. 几何变换
print("\n5. 几何变换")
rotated = processor.rotate_image(original, 45)
print(f"旋转45度完成,形状:{rotated.shape}")
# 6. 直方图均衡化
print("\n6. 直方图均衡化")
equalized = processor.histogram_equalization(original)
# 比较均衡化前后的统计信息
orig_stats = processor.analyze_image(original)
eq_stats = processor.analyze_image(equalized)
print("均衡化前后对比:")
for channel in ['Red', 'Green', 'Blue']:
if f'{channel}_std' in orig_stats:
print(f"{channel}通道标准差:{orig_stats[f'{channel}_std']:.3f} → "
f"{eq_stats[f'{channel}_std']:.3f}")
# 7. 批量处理演示
print("\n7. 批量处理演示")
operations = [
('brightness', {'factor': 1.2}),
('blur', {'kernel_size': 3, 'sigma': 0.8}),
('sharpen', {'strength': 0.3})
]
batch_result = original.copy()
for op_name, params in operations:
if op_name == 'brightness':
batch_result = processor.adjust_brightness(batch_result, **params)
else:
batch_result = processor.apply_filter(batch_result, op_name, **params)
print(f"应用{op_name}操作:范围[{batch_result.min():.3f}, {batch_result.max():.3f}]")
print("\n=== 图像处理演示完成 ===")
return processor, {
'original': original,
'bright': bright_image,
'contrast': contrast_image,
'gray': gray_image,
'blurred': blurred,
'sharpened': sharpened,
'edges': edges,
'embossed': embossed,
'rotated': rotated,
'equalized': equalized,
'batch_processed': batch_result
}
# 运行图像处理演示
processor, processed_images = demonstrate_image_processing()
# 性能测试
def performance_test():
"""NumPy在图像处理中的性能测试"""
print("\n=== 性能测试 ===")
import time
# 创建大图像
large_image = processor.create_sample_image(size=(512, 512), pattern='gradient')
print(f"大图像形状:{large_image.shape}")
print(f"总像素数:{large_image.size:,}")
# 测试不同操作的性能
operations = [
('亮度调整', lambda: processor.adjust_brightness(large_image, 1.2)),
('RGB转灰度', lambda: processor.rgb_to_grayscale(large_image)),
('高斯模糊', lambda: processor.apply_filter(large_image, 'blur', kernel_size=5)),
('边缘检测', lambda: processor.apply_filter(large_image, 'edge_detect')),
]
for op_name, op_func in operations:
start_time = time.time()
result = op_func()
end_time = time.time()
processing_time = end_time - start_time
pixels_per_second = large_image.size / processing_time
print(f"{op_name}:{processing_time:.3f}秒,{pixels_per_second:,.0f} 像素/秒")
performance_test()

5.7 学习总结与进阶指导
> 核心概念掌握检查
通过本课学习,我们掌握了NumPy的核心概念和实际应用。从数组创建到向量化运算,从广播机制到线性代数,每个概念都直接关系到后续机器学习和数据科学的学习。
关键技能对比表:
技能领域 | Python原生 | NumPy优势 | 应用场景 |
---|---|---|---|
数值计算 | 需要循环 | 向量化运算 | 大规模数值运算 |
内存使用 | 高开销 | 连续内存存储 | 大数据处理 |
运算速度 | 慢 | C语言级别优化 | 实时数据处理 |
多维数组 | 需要嵌套列表 | 原生支持 | 图像、矩阵运算 |
线性代数 | 需要手动实现 | 完整函数库 | 机器学习算法 |
> 向量化思维的建立
向量化不仅仅是技术技巧,更是一种编程思维的转变。从"逐个处理"转向"批量操作",这种思维模式将在整个数据科学职业生涯中发挥重要作用。
思维转换示例:
- 传统思维:遍历每个元素进行计算
- 向量化思维:将操作应用于整个数组
- 广播思维:理解不同形状数组间的运算规则
- 矩阵思维:将问题抽象为线性代数运算
> 实践建议与扩展方向
建议通过以下方式深化学习:定期练习不同形状数组的广播运算,熟练掌握索引和切片的各种组合,深入理解线性代数运算在机器学习中的应用。重点培养用NumPy思维分析和解决数值计算问题的能力。
> 与后续课程的连接
NumPy是整个Python数据科学生态系统的基础。Pandas的DataFrame本质上是建立在NumPy数组之上的,Matplotlib的图形绘制需要NumPy数组作为数据输入,而scikit-learn等机器学习库更是将NumPy作为数据交换的标准接口。
掌握NumPy不仅为学习这些高级库铺平了道路,更重要的是建立了处理多维数据的基础思维框架。在后续的机器学习课程中,我们会看到NumPy如何成为连接数据处理、特征工程和模型训练的桥梁。
附录:专业术语表
向量化(Vectorization):将操作应用于整个数组而非逐个元素的编程技术,大大提高运算效率
广播(Broadcasting):NumPy中允许不同形状数组进行运算的机制,通过隐式扩展较小数组的维度
ndarray:NumPy的核心数据结构,表示N维数组对象,支持高效的数值计算
轴(Axis):数组的维度方向,axis=0表示行方向,axis=1表示列方向,用于指定运算的方向
切片(Slicing):通过索引范围访问数组子集的方法,支持步长和多维操作
花式索引(Fancy Indexing):使用整数数组或布尔数组进行数组索引的高级技术
布尔索引(Boolean Indexing):使用布尔数组作为索引来筛选满足条件的元素
形状(Shape):描述数组各维度大小的元组,如(3,4)表示3行4列的二维数组
数据类型(dtype):指定数组中元素的数据类型,如int32、float64、bool等
线性代数(Linear Algebra):处理向量和矩阵运算的数学分支,是机器学习的基础
特征值分解(Eigenvalue Decomposition):将方阵分解为特征值和特征向量的数学方法
奇异值分解(SVD):将矩阵分解为三个矩阵乘积的重要数学工具,广泛用于降维和数据压缩
卷积(Convolution):信号处理中的基本运算,在图像处理和神经网络中有重要应用
直方图均衡化(Histogram Equalization):通过重新分布像素值来增强图像对比度的图像处理技术