【深度学习-Day 6】掌握 NumPy:ndarray 创建、索引、运算与性能优化指南
Langchain系列文章目录
01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来
Python系列文章目录
PyTorch系列文章目录
机器学习系列文章目录
深度学习系列文章目录
Java系列文章目录
JavaScript系列文章目录
深度学习系列文章目录
01-【深度学习-Day 1】为什么深度学习是未来?一探究竟AI、ML、DL关系与应用
02-【深度学习-Day 2】图解线性代数:从标量到张量,理解深度学习的数据表示与运算
03-【深度学习-Day 3】搞懂微积分关键:导数、偏导数、链式法则与梯度详解
04-【深度学习-Day 4】掌握深度学习的“概率”视角:基础概念与应用解析
05-【深度学习-Day 5】Python 快速入门:深度学习的“瑞士军刀”实战指南
06-【深度学习-Day 6】掌握 NumPy:ndarray 创建、索引、运算与性能优化指南
文章目录
- Langchain系列文章目录
- Python系列文章目录
- PyTorch系列文章目录
- 机器学习系列文章目录
- 深度学习系列文章目录
- Java系列文章目录
- JavaScript系列文章目录
- 深度学习系列文章目录
- 前言
- 一、初识 NumPy:为何选择它?
- 1.1 Python 列表的局限性
- 1.2 NumPy 的核心优势
- 二、NumPy 核心:ndarray 对象
- 2.1 创建 ndarray
- 2.1.1 从 Python 列表或元组创建
- 2.1.2 使用 NumPy 内建函数创建
- 2.2 ndarray 的重要属性
- 三、ndarray 的索引与切片
- 3.1 基础索引
- 3.2 切片操作
- 3.2.1 一维数组切片
- 3.2.2 多维数组切片
- 3.2.3 切片的视图 (View) vs 副本 (Copy)
- 3.3 布尔索引与花式索引
- 3.3.1 布尔索引 (Boolean Indexing)
- 3.3.2 花式索引 (Fancy Indexing)
- 四、NumPy 的核心运算
- 4.1 元素级运算 (Element-wise Operations)
- 4.2 聚合运算 (Aggregation Operations)
- 4.3 线性代数运算
- 五、广播机制 (Broadcasting)
- 5.1 什么是广播?
- 5.2 广播规则
- 5.3 广播实例
- 六、NumPy 实战:性能对比
- 6.1 场景设定
- 6.2 纯 Python 实现
- 6.3 NumPy 实现
- 6.4 性能比较
- 七、总结
前言
欢迎来到深度学习之旅的第六天!在前几天的学习中,我们已经了解了深度学习的基本概念,并掌握了 Python 语言的基础(或者复习了它)。从今天开始,我们将接触到数据科学领域至关重要的工具库。而 NumPy (Numerical Python) 无疑是其中最基础、最核心的一个。
为什么 NumPy 如此重要?想象一下,深度学习本质上就是对大量的数字(如图像像素、词向量、模型参数)进行复杂的数学运算。NumPy 提供了一个强大的 N 维数组对象 ndarray
,以及一系列用于高效处理这些数组的函数。它不仅运算速度远超纯 Python 实现,更是 SciPy、Pandas、Matplotlib、Scikit-learn 等众多高级数据科学库的底层依赖。可以说,掌握 NumPy 是进行数据分析、机器学习乃至深度学习的必备技能。
本篇文章将带你深入探索 NumPy 的世界,从核心对象 ndarray
的创建与属性,到灵活的索引与切片,再到强大的数组运算和广播机制,最后通过实战对比展示其性能优势。无论你是数据科学新手,还是希望巩固 NumPy 基础的开发者,相信都能从中获益。
一、初识 NumPy:为何选择它?
在我们深入 NumPy 的细节之前,首先要理解为什么我们需要它。Python 原生的列表(List)虽然灵活,但在处理大规模数值计算时,存在一些显著的局限性。
1.1 Python 列表的局限性
Python 列表可以包含任意类型的对象,这种灵活性带来了内存开销和计算效率上的问题:
- 内存消耗大: 列表中存储的是对象的引用,而非数据本身。对于大量数值数据,这会占用过多内存。
- 计算效率低: 对列表中的元素进行数学运算通常需要显式的循环,这在 Python 解释器层面执行效率较低,尤其是在处理大规模数据时。
让我们看一个简单的例子,计算两个列表对应元素的和:
# 纯 Python 实现列表元素求和
list_a = [1, 2, 3, 4, 5]
list_b = [6, 7, 8, 9, 10]
result = []
for i in range(len(list_a)):result.append(list_a[i] + list_b[i])
print(f"纯 Python 列表求和结果: {result}")
# 输出: 纯 Python 列表求和结果: [7, 9, 11, 13, 15]# 如果数据量巨大,这个循环会非常慢
large_list_a = list(range(1000000))
large_list_b = list(range(1000000))# 尝试计算 (仅为说明,实际运行时会比较耗时)
# start_time = time.time()
# large_result = [large_list_a[i] + large_list_b[i] for i in range(len(large_list_a))]
# end_time = time.time()
# print(f"处理大型列表耗时: {end_time - start_time:.4f} 秒") # 实际运行会显示具体时间
1.2 NumPy 的核心优势
为了解决上述问题,NumPy 应运而生,它提供了以下核心优势:
- 高效的 N 维数组对象 (ndarray): NumPy 的核心是
ndarray
,它是一个存储 同类型 元素的多维数组。由于元素类型相同且在内存中连续存储,NumPy 可以利用优化过的 C 语言底层代码进行快速计算,极大地提高了运算效率和内存使用效率。 - 矢量化运算 (Vectorization): NumPy 允许你直接对整个数组执行数学运算,而无需编写显式循环。这种矢量化操作不仅代码简洁,而且执行速度极快,因为它利用了底层的 C 实现和 CPU 的 SIMD(Single Instruction, Multiple Data)指令。
- 广播 (Broadcasting) 功能: NumPy 能够自动处理不同形状数组之间的运算,使得代码更加灵活简洁。
- 丰富的函数库: 提供了大量用于线性代数、傅里叶变换、随机数生成等的数学函数。
二、NumPy 核心:ndarray 对象
ndarray
(N-dimensional array) 是 NumPy 库的基石。让我们学习如何创建它以及了解它的重要属性。
2.1 创建 ndarray
创建 ndarray
的方式多种多样,以下是一些常用的方法:
2.1.1 从 Python 列表或元组创建
最常见的方式是使用 np.array()
函数将 Python 的列表或元组转换为 ndarray
。
import numpy as np # 约定俗成的导入方式# 从列表创建一维数组
list_data = [1, 2, 3, 4, 5]
arr1d = np.array(list_data)
print(f"从列表创建的一维数组:\n{arr1d}")
print(f"数组类型: {type(arr1d)}") # <class 'numpy.ndarray'># 从嵌套列表创建二维数组 (矩阵)
nested_list_data = [[1, 2, 3], [4, 5, 6]]
arr2d = np.array(nested_list_data)
print(f"\n从嵌套列表创建的二维数组:\n{arr2d}")
2.1.2 使用 NumPy 内建函数创建
NumPy 提供了一些函数用于创建特定类型的数组:
np.zeros(shape)
: 创建指定形状 (shape) 且所有元素为 0 的数组。np.ones(shape)
: 创建指定形状且所有元素为 1 的数组。np.full(shape, fill_value)
: 创建指定形状且所有元素为指定值fill_value
的数组。np.arange(start, stop, step)
: 类似于 Python 的range()
,创建等差数列数组。np.linspace(start, stop, num)
: 创建包含num
个元素的等间隔数列数组,包含start
和stop
。np.random.rand(d0, d1, ..., dn)
: 创建指定形状的、元素在 [0, 1) 之间均匀分布的随机数组。np.random.randn(d0, d1, ..., dn)
: 创建指定形状的、元素服从标准正态分布(均值为0,方差为1)的随机数组。np.eye(N)
或np.identity(N)
: 创建一个 N 阶单位矩阵。
# 创建全零数组 (2行3列)
zeros_arr = np.zeros((2, 3))
print(f"\n全零数组:\n{zeros_arr}")# 创建全一数组 (一维,长度为4)
ones_arr = np.ones(4)
print(f"\n全一数组:\n{ones_arr}")# 创建等差数列数组
arange_arr = np.arange(0, 10, 2) # 从0开始,到10结束(不包含),步长为2
print(f"\n等差数列数组:\n{arange_arr}")# 创建等间隔数列数组
linspace_arr = np.linspace(0, 1, 5) # 从0到1,均匀取5个数
print(f"\n等间隔数列数组:\n{linspace_arr}")# 创建 3x3 的随机数组 (均匀分布)
rand_arr = np.random.rand(3, 3)
print(f"\n3x3 随机数组 (均匀分布):\n{rand_arr}")# 创建 2x4 的随机数组 (标准正态分布)
randn_arr = np.random.randn(2, 4)
print(f"\n2x4 随机数组 (标准正态分布):\n{randn_arr}")# 创建 3x3 单位矩阵
identity_matrix = np.eye(3)
print(f"\n3x3 单位矩阵:\n{identity_matrix}")
2.2 ndarray 的重要属性
了解 ndarray
的属性有助于我们更好地操作和理解数组:
ndim
: 数组的维数(轴的数量)。例如,一维数组ndim
为 1,二维数组(矩阵)ndim
为 2。shape
: 数组的维度。返回一个元组,表示数组在每个维度上的大小。例如,一个 2 行 3 列的矩阵,shape
为(2, 3)
。size
: 数组中元素的总个数,等于shape
元组中各元素的乘积。dtype
: 数组中元素的数据类型。NumPy 支持多种数据类型(如int32
,float64
,bool
等),这对于优化内存和计算至关重要。itemsize
: 数组中每个元素占用的字节数。data
: 指向数组数据内存的缓冲区。通常我们不需要直接操作它。
arr = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
print(f"\n示例数组:\n{arr}")print(f"数组维数 (ndim): {arr.ndim}") # 输出: 2
print(f"数组形状 (shape): {arr.shape}") # 输出: (2, 3)
print(f"元素总数 (size): {arr.size}") # 输出: 6
print(f"元素类型 (dtype): {arr.dtype}") # 输出: float64
print(f"每个元素字节数 (itemsize): {arr.itemsize}") # 输出: 8 (float64 占 8 字节)
我们可以使用 astype()
方法显式地转换数组的数据类型:
float_arr = np.array([1.1, 2.7, 3.5])
print(f"\n原始浮点数组: {float_arr}, 类型: {float_arr.dtype}")# 转换为整数类型 (小数部分会被截断)
int_arr = float_arr.astype(np.int32)
print(f"转换后的整数数组: {int_arr}, 类型: {int_arr.dtype}")
三、ndarray 的索引与切片
掌握如何访问和修改 ndarray
中的元素是 NumPy 操作的基础。NumPy 提供了比 Python 列表更强大和灵活的索引机制。
3.1 基础索引
对于一维数组,索引方式与 Python 列表类似,使用方括号 []
和从 0 开始的下标。
arr1d = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
print(f"\n一维数组: {arr1d}")# 获取第一个元素
print(f"第一个元素: {arr1d[0]}") # 输出: 0# 获取第五个元素
print(f"第五个元素: {arr1d[4]}") # 输出: 4# 修改元素
arr1d[0] = 100
print(f"修改后的数组: {arr1d}") # 输出: [100 1 2 3 4 5 6 7 8 9]
对于多维数组(以二维为例),可以使用逗号分隔的索引元组 arr[row, column]
来访问特定元素。
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(f"\n二维数组:\n{arr2d}")# 获取第1行第2列的元素 (索引从0开始)
element = arr2d[0, 1]
print(f"第1行第2列的元素: {element}") # 输出: 2# 也可以使用分开的方括号 (效果相同,但不推荐)
element_alt = arr2d[0][1]
print(f"另一种方式获取元素: {element_alt}") # 输出: 2# 修改第3行第1列的元素
arr2d[2, 0] = 77
print(f"修改后的二维数组:\n{arr2d}")
3.2 切片操作
切片(Slicing)允许我们获取数组的子集(子数组)。其语法是 start:stop:step
,与 Python 列表切片类似,但不包含 stop
索引。
3.2.1 一维数组切片
arr1d = np.arange(10) # [ 0 1 2 3 4 5 6 7 8 9]
print(f"\n原始一维数组: {arr1d}")# 获取索引 2 到 5 (不含) 的元素
slice1 = arr1d[2:5]
print(f"arr1d[2:5]: {slice1}") # 输出: [2 3 4]# 获取从头开始到索引 5 (不含) 的元素
slice2 = arr1d[:5]
print(f"arr1d[:5]: {slice2}") # 输出: [0 1 2 3 4]# 获取从索引 5 到末尾的元素
slice3 = arr1d[5:]
print(f"arr1d[5:]: {slice3}") # 输出: [5 6 7 8 9]# 获取所有元素 (步长为2)
slice4 = arr1d[::2]
print(f"arr1d[::2]: {slice4}") # 输出: [0 2 4 6 8]
3.2.2 多维数组切片
可以在每个维度上分别进行切片。
arr2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print(f"\n原始二维数组:\n{arr2d}")# 获取前两行 (索引0和1)
slice2d_1 = arr2d[:2]
print(f"\n前两行:\n{slice2d_1}")# 获取前两行,以及第2列到第4列 (不含) 的数据
slice2d_2 = arr2d[:2, 1:3]
print(f"\n前两行,第2、3列:\n{slice2d_2}")# 获取第一行的所有列
slice2d_3 = arr2d[0, :] # 或者 arr2d[0]
print(f"\n第一行:\n{slice2d_3}")# 获取第二列的所有行
slice2d_4 = arr2d[:, 1]
print(f"\n第二列:\n{slice2d_4}")
3.2.3 切片的视图 (View) vs 副本 (Copy)
非常重要的一点: NumPy 数组的切片默认返回的是原始数组的 视图 (View),而不是副本 (Copy)。这意味着对视图的修改 会影响 原始数组。
arr = np.arange(5) # [0 1 2 3 4]
print(f"\n原始数组: {arr}")# 创建切片 (视图)
arr_slice = arr[1:4] # [1 2 3]
print(f"切片: {arr_slice}")# 修改切片中的元素
arr_slice[0] = 99
print(f"修改切片后,切片变为: {arr_slice}") # 输出: [99 2 3]
print(f"修改切片后,原始数组变为: {arr}") # 输出: [ 0 99 2 3 4] <--- 原始数组被修改了!# 如果需要副本而不是视图,可以使用 .copy() 方法
arr_copy = arr[1:4].copy()
print(f"\n创建副本: {arr_copy}")
arr_copy[0] = 111 # 修改副本
print(f"修改副本后,副本变为: {arr_copy}") # 输出: [111 2 3]
print(f"修改副本后,原始数组保持不变: {arr}") # 输出: [ 0 99 2 3 4]
理解视图和副本的区别对于避免意外修改数据至关重要。
3.3 布尔索引与花式索引
NumPy 还支持更高级的索引方式。
3.3.1 布尔索引 (Boolean Indexing)
我们可以使用一个布尔类型的数组来选择元素。布尔数组的形状通常与原数组相同,True
对应的位置的元素会被选中。
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
data = np.random.randn(7, 4) # 假设是与 names 对应的数据
print(f"\nNames 数组: {names}")
print(f"Data 数组:\n{data}")# 创建布尔数组,判断 names 是否等于 'Bob'
is_bob = (names == 'Bob')
print(f"\n布尔数组 (names == 'Bob'): {is_bob}") # 输出: [ True False False True False False False]# 使用布尔数组选择 data 中对应的行
bob_data = data[is_bob]
print(f"\n选择 'Bob' 对应的行数据:\n{bob_data}")# 也可以结合布尔运算 (&, |, ~)
is_bob_or_will = (names == 'Bob') | (names == 'Will')
print(f"\n布尔数组 (names == 'Bob' or names == 'Will'): {is_bob_or_will}")
bob_or_will_data = data[is_bob_or_will]
print(f"\n选择 'Bob' 或 'Will' 对应的行数据:\n{bob_or_will_data}")# 也可以用布尔数组来赋值
data[names != 'Joe'] = 0 # 将非 'Joe' 对应的行设置为 0
print(f"\n将非 'Joe' 行设置为 0 后的 Data 数组:\n{data}")
3.3.2 花式索引 (Fancy Indexing)
花式索引使用一个整数数组(或列表)作为索引,来选择特定的行、列或元素。
arr = np.zeros((8, 4))
for i in range(8):arr[i] = i # 给每行赋不同的值
print(f"\n原始 8x4 数组:\n{arr}")# 选择第 4, 3, 0, 6 行 (注意顺序)
selected_rows = arr[[4, 3, 0, 6]]
print(f"\n选择第 4, 3, 0, 6 行:\n{selected_rows}")# 使用负数索引 (从末尾开始)
selected_rows_neg = arr[[-1, -3, -5]] # 选择最后一行、倒数第三行、倒数第五行
print(f"\n使用负数索引选择行:\n{selected_rows_neg}")# 选择特定行列的元素
# 假设要选择 (1, 0), (5, 3), (7, 1) 三个位置的元素
rows = np.array([1, 5, 7])
cols = np.array([0, 3, 1])
selected_elements = arr[rows, cols]
print(f"\n选择 (1, 0), (5, 3), (7, 1) 元素: {selected_elements}")
# 输出: [1. 5. 7.] (分别是 arr[1,0], arr[5,3], arr[7,1])
注意: 花式索引返回的是数据的 副本 (Copy),而不是视图。
四、NumPy 的核心运算
NumPy 的真正威力在于其丰富的、高效的数组运算功能。
4.1 元素级运算 (Element-wise Operations)
对数组执行算术运算(加、减、乘、除、幂等)时,NumPy 会自动将运算应用到数组的 每个元素 上。这称为元素级运算或矢量化运算。
arr1 = np.array([[1., 2., 3.], [4., 5., 6.]])
arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
print(f"\n数组 arr1:\n{arr1}")
print(f"数组 arr2:\n{arr2}")# 元素级加法
print(f"\narr1 + arr2:\n{arr1 + arr2}")# 元素级乘法
print(f"\narr1 * arr2:\n{arr1 * arr2}")# 标量乘法 (标量会广播到数组的每个元素)
print(f"\narr1 * 0.5:\n{arr1 * 0.5}")# 元素级比较 (返回布尔数组)
print(f"\narr1 > arr2:\n{arr1 > arr2}")# NumPy 通用函数 (ufunc)
# 这些函数也执行元素级操作
print(f"\n对 arr1 开平方根:\n{np.sqrt(arr1)}")
print(f"\n对 arr1 计算指数:\n{np.exp(arr1)}")
对比纯 Python 实现,NumPy 的元素级运算极其高效简洁。
4.2 聚合运算 (Aggregation Operations)
NumPy 提供了许多用于计算数组统计信息的聚合函数,如求和、平均值、最大值、最小值等。
np.sum()
: 计算数组元素之和。np.mean()
: 计算数组元素的平均值。np.std()
: 计算数组元素的标准差。np.var()
: 计算数组元素的方差。np.min()
: 找出数组的最小值。np.max()
: 找出数组的最大值。np.argmin()
: 找出数组最小值的索引。np.argmax()
: 找出数组最大值的索引。np.cumsum()
: 计算元素的累积和。np.cumprod()
: 计算元素的累积积。
这些函数可以作用于整个数组,也可以沿着指定的 轴 (axis) 进行计算。
- 对于二维数组:
axis=0
表示沿着行的方向(计算每列的统计值),axis=1
表示沿着列的方向(计算每行的统计值)。
arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
print(f"\n聚合运算示例数组:\n{arr}")# 计算所有元素的和
print(f"所有元素之和: {np.sum(arr)}") # 输出: 36 (或 arr.sum())# 计算每列的和 (沿着行的方向, axis=0)
print(f"每列的和 (axis=0): {np.sum(arr, axis=0)}") # 输出: [ 9 12 15] (或 arr.sum(axis=0))# 计算每行的平均值 (沿着列的方向, axis=1)
print(f"每行的平均值 (axis=1): {np.mean(arr, axis=1)}") # 输出: [1. 4. 7.] (或 arr.mean(axis=1))# 找出整个数组的最大值
print(f"最大值: {np.max(arr)}") # 输出: 8 (或 arr.max())# 找出每行最大值的索引 (axis=1)
print(f"每行最大值的索引 (axis=1): {np.argmax(arr, axis=1)}") # 输出: [2 2 2] (或 arr.argmax(axis=1))
4.3 线性代数运算
NumPy 提供了 linalg
模块,包含丰富的线性代数运算功能,这对于机器学习和深度学习至关重要(回顾 Day 2 的线性代数!)。
np.dot(a, b)
或a @ b
: 矩阵乘法(对于二维数组)或向量内积(对于一维数组)。arr.T
或np.transpose(arr)
: 矩阵转置。np.linalg.inv(arr)
: 计算矩阵的逆。np.linalg.det(arr)
: 计算矩阵的行列式。np.linalg.eig(arr)
: 计算矩阵的特征值和特征向量。np.linalg.svd(arr)
: 计算奇异值分解 (SVD)。
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
v = np.array([9, 10])
print(f"\n矩阵 A:\n{A}")
print(f"矩阵 B:\n{B}")
print(f"向量 v: {v}")# 矩阵乘法
dot_product = np.dot(A, B)
# 或者使用 @ 运算符 (Python 3.5+)
dot_product_alt = A @ B
print(f"\n矩阵乘法 A @ B:\n{dot_product}")
print(f"矩阵乘法 A @ B (使用@):\n{dot_product_alt}")# 矩阵与向量乘法
mat_vec_product = A @ v
print(f"\n矩阵向量乘法 A @ v: {mat_vec_product}") # 输出: [29 67]# 矩阵转置
A_transpose = A.T
print(f"\n矩阵 A 的转置:\n{A_transpose}")# 计算矩阵 A 的行列式
det_A = np.linalg.det(A)
print(f"\n矩阵 A 的行列式: {det_A:.2f}") # 输出: -2.00# 计算矩阵 A 的逆
inv_A = np.linalg.inv(A)
print(f"\n矩阵 A 的逆:\n{inv_A}")# 验证逆矩阵 A @ inv(A) 约等于单位矩阵
identity_check = A @ inv_A
print(f"\nA @ inv(A) (应接近单位矩阵):\n{np.round(identity_check)}") # 使用 round 消除微小误差
五、广播机制 (Broadcasting)
广播是 NumPy 中一项强大的机制,它允许 NumPy 在执行元素级运算时,自动扩展(或“广播”)较小数组的维度,以匹配较大数组的形状,而无需显式地创建扩展后的数组副本。这使得代码更简洁,内存效率更高。
5.1 什么是广播?
想象一下你想让一个数组的每个元素都加上一个相同的标量值。NumPy 允许你直接写 arr + scalar
。实际上,NumPy 会将标量 scalar
“广播”成一个与 arr
形状相同的数组,然后执行元素级加法。
arr = np.array([1, 2, 3])
scalar = 10# NumPy 自动广播标量
result = arr + scalar
print(f"\n数组加标量 (广播):\n{result}") # 输出: [11 12 13]
# 相当于 NumPy 内部执行了类似下面的操作:
# broadcasted_scalar = np.array([10, 10, 10])
# result = arr + broadcasted_scalar
5.2 广播规则
并非所有不同形状的数组都能进行广播。NumPy 遵循一套严格的规则来确定两个数组是否兼容:
规则 1: 如果两个数组的维数 ndim
不同,那么在较小数组的 shape
前面补 1,直到它们的维数相同。
规则 2: 比较两个数组 从末尾维度开始 的各个轴的长度:
* 如果两个数组在某个轴上的长度相同,或者
* 其中一个数组在某个轴上的长度为 1,
那么认为它们在这个轴上是兼容的。
规则 3: 如果在所有轴上都兼容,则可以进行广播。
规则 4: 广播后结果数组的形状是两个输入数组在各个轴上长度的 最大值。
规则 5: 如果不满足以上条件,则会引发 ValueError
。
图示理解 (以两个二维数组为例):
假设数组 A 的 shape 为 (3, 4)
,数组 B 的 shape 为 (1, 4)
。
- 维数相同,都是 2。
- 比较末尾轴(轴 1):长度都是 4,兼容。
- 比较前一个轴(轴 0):A 的长度是 3,B 的长度是 1,兼容(因为有一个是 1)。
- 兼容,可以广播。
- 结果数组的 shape 为
(max(3, 1), max(4, 4))
,即(3, 4)
。NumPy 会将 B 的第一行“复制”3次,使其形状变为(3, 4)
,然后与 A 进行运算。
假设数组 C 的 shape 为 (3, 4)
,数组 D 的 shape 为 (3, 1)
。
- 维数相同,都是 2。
- 比较末尾轴(轴 1):C 的长度是 4,D 的长度是 1,兼容。
- 比较前一个轴(轴 0):长度都是 3,兼容。
- 兼容,可以广播。
- 结果数组的 shape 为
(max(3, 3), max(4, 1))
,即(3, 4)
。NumPy 会将 D 的第一列“复制”4次,使其形状变为(3, 4)
,然后与 C 进行运算。
假设数组 E 的 shape 为 (3, 4)
,数组 F 的 shape 为 (2, 4)
。
- 维数相同,都是 2。
- 比较末尾轴(轴 1):长度都是 4,兼容。
- 比较前一个轴(轴 0):E 的长度是 3,F 的长度是 2,不兼容(既不相等,也没有一个是 1)。
- 无法广播,会报错。
5.3 广播实例
# 示例 1: 二维数组加一维数组
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr1d = np.array([10, 20, 30])
print(f"\n二维数组:\n{arr2d}") # shape (3, 3)
print(f"一维数组: {arr1d}") # shape (3,)# arr1d 会被广播到 arr2d 的每一行
result = arr2d + arr1d
print(f"\n广播加法 (二维 + 一维):\n{result}")
# [[11 22 33]
# [14 25 36]
# [17 28 39]]# 示例 2: 需要调整形状以利用广播
# 假设想让 arr2d 的每一列都加上 [100, 200, 300]
col_vector = np.array([[100], [200], [300]]) # shape (3, 1)
print(f"\n列向量:\n{col_vector}")# col_vector 会被广播到 arr2d 的每一列
result_col = arr2d + col_vector
print(f"\n广播加法 (二维 + 列向量):\n{result_col}")
# [[101 102 103]
# [204 205 206]
# [307 308 309]]
广播是 NumPy 中一个极其有用的特性,但初学者可能会觉得有些困惑。多动手实践,结合广播规则进行思考,就能逐渐掌握它。
六、NumPy 实战:性能对比
理论说了这么多,NumPy 的高性能究竟体现在哪里?让我们通过一个简单的实验来直观感受一下。
6.1 场景设定
我们将创建一个包含一百万个随机数的大型列表和对应的 NumPy 数组,然后分别使用纯 Python 循环和 NumPy 矢量化运算来计算每个元素的平方,并比较所花费的时间。
6.2 纯 Python 实现
import time
import random# 创建大型 Python 列表
n_elements = 1000000
python_list = [random.random() for _ in range(n_elements)]# 使用纯 Python 循环计算平方
start_time_py = time.time()
result_py = []
for x in python_list:result_py.append(x * x)
end_time_py = time.time()print(f"\n--- 性能对比 ---")
print(f"纯 Python 循环处理 {n_elements} 个元素耗时: {end_time_py - start_time_py:.4f} 秒")
6.3 NumPy 实现
# 创建等效的 NumPy 数组
numpy_array = np.array(python_list) # 或者直接 np.random.rand(n_elements)# 使用 NumPy 矢量化运算计算平方
start_time_np = time.time()
result_np = numpy_array * numpy_array # 或者 np.square(numpy_array)
end_time_np = time.time()print(f"NumPy 矢量化运算处理 {n_elements} 个元素耗时: {end_time_np - start_time_np:.4f} 秒")
6.4 性能比较
运行上述代码(具体时间会因机器性能而异),你会发现 NumPy 的矢量化运算速度通常比纯 Python 循环快 几十倍甚至上百倍!
--- 性能对比 ---
纯 Python 循环处理 1000000 个元素耗时: 0.1234 秒 # 示例时间,实际会变化
NumPy 矢量化运算处理 1000000 个元素耗时: 0.0025 秒 # 示例时间,实际会变化
这个简单的对比清晰地展示了 NumPy 在数值计算方面的巨大性能优势。这正是为什么它成为 Python 数据科学生态系统不可或缺的一部分。在处理大规模数据时,优先考虑使用 NumPy 的矢量化操作是提高效率的关键。
七、总结
恭喜你完成了 NumPy 核心知识的学习!通过本篇文章,我们系统地探讨了 NumPy 的关键特性与应用:
- NumPy 的价值: 我们理解了 Python 列表在数值计算上的局限性,以及 NumPy 通过高效的
ndarray
对象、矢量化运算和广播机制带来的性能和便利性优势,它是 Python 科学计算的基石。 - ndarray 对象: 掌握了多种创建
ndarray
的方法(从列表、使用zeros/ones/arange/linspace/random
等),并熟悉了其重要属性(ndim
,shape
,size
,dtype
)。 - 索引与切片: 学会了使用基础索引、切片(区分视图与副本)、布尔索引和花式索引来灵活地访问和操作数组数据。
- 核心运算: 掌握了 NumPy 的核心运算能力,包括高效的元素级运算(矢量化)、强大的聚合运算(
sum
,mean
,max
等,可指定axis
)以及基础的线性代数运算(矩阵乘法、转置、求逆等)。 - 广播机制: 理解了广播的概念、工作规则及其在处理不同形状数组运算时的便利性。
- 性能优势: 通过实战对比,直观感受了 NumPy 矢量化运算相对于纯 Python 循环的巨大性能提升。
熟练掌握 NumPy 是后续学习 Pandas、Matplotlib、Scikit-learn 乃至 TensorFlow、PyTorch 等深度学习框架的重要前提。务必多加练习,将今天学习的知识运用到实际操作中。在下一篇文章中,我们将继续探索数据处理的另一大利器——Pandas 库!敬请期待!
相关文章:
【深度学习-Day 6】掌握 NumPy:ndarray 创建、索引、运算与性能优化指南
Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...
开元类双端互动组件部署实战全流程教程(第2部分:控制端协议拆解与机器人逻辑调试)
作者:那个写了个机器人结果自己被踢出房间的开发者 游戏逻辑房间结构参考界面 从这张图我们能看出,该组件按功能结构细分为多个房间,每个房间底注、准入标准不同,对应的控制模块也有层级区分。常规来说,一个“互动房间…...
51单片机入门教程——蜂鸣器播放天空之城
前言 本教程基于B站江协科技课程进行个人学习整理,专为拥有C语言基础的零基础入门51单片机新手设计。既帮助解决因时间差导致的设备迭代调试难题,也助力新手快速掌握51单片机核心知识,实现从C语言理论到单片机实践应用的高效过渡 。 目录 …...
linux 历史记录命令
命令方式 #/bin/bash #cd /tmp saveFile"tmp.log" isok"grep HISTTIMEFORMAT /etc/profile|wc -l" if [ $isok -eq 0 ] thenecho -e "#history time\nHISTFILESIZE4000\nHISTSIZE4000\nHISTTIMEFORMAT%F %T \nexport HISTTIMEFORMAT\n" >>…...
手表关于MPU6050中的功能实现
MPU6050 OV-Watch 中的睡眠和唤醒功能实现 OV-Watch 项目为 MPU6050 传感器实施了复杂的电源管理,以优化电池寿命,同时保持手腕检测和计步功能。以下是对睡眠和唤醒机制的详细分析: 内核休眠/唤醒功能实现 MPU6050 有两个主要功能来控制其…...
Qt中数据结构使用自定义类————附带详细示例
文章目录 C对数据结构使用自定义类1 QMap使用自定义类1.1 使用自定义类做key1.2 使用自定义类做value 2 QSet使用自定义类 参考 C对数据结构使用自定义类 1 QMap使用自定义类 1.1 使用自定义类做key QMap<key,value>中数据存入时会对存入key值的数据进行比较ÿ…...
深入浅出数据库的函数依赖关系
数据库的“恋爱关系”:函数依赖的那些事儿 在数据库的世界里,属性之间也存在“恋爱关系”。有些属性是“灵魂伴侣”,彼此绑定;有些是“单向奔赴”,只能依赖对方;还有些是“三角恋”,通过中间人…...
C语言易混淆知识点详解
C语言中容易混淆的知识点详解 C语言作为一门基础且强大的编程语言,有许多容易混淆的概念和特性。以下是C语言中一些常见易混淆知识点的详细解析: 1. 指针与数组 相似点: c 复制 下载 int arr[10]; int *ptr arr; 都可以使用[]运算符访…...
如何选择合适的光源?
目录 工业相机光源类型全面指南 1. 环形光源及其变体 高角度环形光源 优点 缺点 典型应用场景 低角度环形光源(暗场照明) 优点 缺点 典型应用场景 2. 条形光源与组合照明系统 技术特点 组合条形光源 优点 缺点 典型应用场景 3. 同轴光源…...
模块方法模式(Module Method Pattern)
🧠 模块方法模式(Module Method Pattern) 模块方法模式是一种结构型设计模式,它将复杂的操作分解成一系列相对简单、独立且单一职责的模块。每个模块负责完成一种具体的操作,其他模块或系统可以通过调用这些模块的公开…...
OpenCV第6课 图像处理之几何变换(仿射)
1.仿射变换 2. 平移 3 旋转 附录A 二维旋转矩阵 附录B 三维旋转矩阵与轴角表示 1.仿射变换 仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够保持图像的平直性和平行性。 平直性是指图像经过仿射变换后,直线仍然是直线,平行性是指图像在…...
【中间件】brpc_基础_TimerThread
文章目录 TimerThread1 简介2 主要设计点2.1 数据结构:分层时间轮(Hierarchical Timing Wheel)2.2 线程模型2.3 任务管理 3 关键代码分析3.1 类定义(timer_thread.h)3.2 时间轮初始化(timer_thread.cpp&…...
拷贝多个Excel单元格区域为图片并粘贴到Word
Excel工作表Sheet1中有两个报表,相应单元格区域分别定义名称为Report1和Report2,如下图所示。 现在需要将图片拷贝图片粘贴到新建的Word文档中。 示例代码如下。 Sub Demo()Dim oWordApp As ObjectDim ws As Worksheet: Set ws ThisWorkbook.Sheets(&…...
网络原理(6)—— 应用层之HTTP协议
目录 一. 应用层 二. 重要应用层协议DNS(Domain Name System) 三. HTTP协议 3.1 HTTP抓包工具 3.2 HTTP格式 3.2.1 请求 3.2.2 响应 3.3 HTTP的工作过程 一. 应用层 应用层协议就像是人们之间的交流规则,它帮助不同的计算机程序(应用)…...
Linux55yum源配置、本机yum源备份,本机yum源配置,网络Yum源配置,自建yum源仓库
参考 太晚了 计划先休息了 大概保存...
250505_HTML
HTML 1. HTML5语法与基础标签1.1 HTML5特性1.1.1 空白折叠现象1.1.2 转义字符 1.2 HTML注释1.3 基础标签1.3.1 div标签1.3.2 标题标签1.3.3 段落标签1.3.1.3.1.3.1.3. 1. HTML5语法与基础标签 1.1 HTML5特性 1.1.1 空白折叠现象 1.1.2 转义字符 1.2 HTML注释 1.3 基础标签 1…...
1. 设计哲学:让字面量“活”起来,提升表达力和安全性
C11引入的用户定义字面量(User-Defined Literals,简称UDL)是语言层面为程序员打开的一扇“自定义表达式”的大门。它允许我们为字面量(比如数字、字符、字符串)添加自定义后缀,从而让代码更具语义化、更易读…...
【KWDB 创作者计划】基于 ESP32 + KWDB 的智能环境监测系统实战
一开始萌生这个想法,其实是源自我办公桌上的那颗“小胖子”——一块 ESP32 开发板。它陪我度过了不少调试夜,也让我对物联网有了真正的感知。恰逢 KaiwuDB 举办征文活动,我便想着,何不将我日常积攒下来的一些硬件和数据库实战经验…...
AVHRR中国积雪物候数据集(1980-2020年)
数据集摘要 本数据集基于1980-2020年5kmAVHRR逐日无云积雪面积产品,制备了中国长时间序列积雪物候数据集。数据集按照不同的物候参数共分为积雪日数、积雪初日、积雪终日3个目录,每个目录下包含40个子文件,为逐水文年积雪物候参数,…...
PCB设计中电感封装的选型
在PCB设计中,电感封装的选型直接影响电路性能、布局效率、热管理能力及系统可靠性。合理的封装选择不仅能优化空间利用率,还能提升电磁兼容性(EMC)和长期稳定性。以下从封装类型、尺寸参数、应用场景适配、布局协同设计、热管理策…...
LintCode第766题,LintCode第1141题,LintCode第478题
第766题描述 判断给出的年份 n 是否为闰年. 如果 n 为闰年则返回 true 闰年是包含额外一天的日历年. 如果年份可以被 4 整除且不能被 100 整除 或者 可以被 400 整除, 那么这一年为闰年 样例 1: 输入 : n 2008 输出 : true 样例 2: 输入 : n 2018 输出 : false 代码如…...
三十一、基于HMM的词性标注
基于HMM的中文词性标注 1 实验目标 理解HMM模型的原理和基本问题理解HMM的实现命名实体识别的具体步骤掌握HMM模型实现命名实体识别的方法 2 实验环境 HMM的中文词性标注的实验环境。 3 实验步骤 该项目主要由3个代码文件组成,分别为hmm.py、tagging.py和run.p…...
MCUboot 中的 BOOT_SWAP_TYPE_PERM 功能介绍
目录 概述 1 Image 数据结构 1.1 Image介绍 1.2 Swap info 2 BOOT_SWAP_TYPE_PERM 功能 2.1 功能定义 2.2 典型工作流程 3 BOOT_SWAP_TYPE_xx的其他功能 3.1 BOOT_SWAP_TYPE_REVERT 3.2 三中模式的比较 4 使用机制 4.1 实现细节 4.2 使用场景 4.3 开发者注意事…...
数学复习笔记 2
前言 朋友和我讨论了一个二重积分题,非常有意思。内容非常细致。整理如下: 二重积分 题目来源是 1000 上面的 16 题,积分区域是一个偏心圆,偏心圆的圆心在 y 轴上面,偏心圆是关于 y 轴对称的,可以看关于…...
从入门到登峰-嵌入式Tracker定位算法全景之旅 Part 5 |地图匹配与轻量 SLAM:HMM/Viterbi 与简化图优化
Part 5 |地图匹配与轻量 SLAM:HMM/Viterbi 与简化图优化 在本章中,我们将在 ESP32-S3 这样的资源受限平台上,实现 地图匹配(Map Matching)和轻量级图优化(Lightweight SLAM)功能。通过 隐马尔可夫模型(HMM)+ Viterbi 算法,以及简化的图优化思路,校正定位轨迹,提升…...
Amazon Bedrock Converse API:开启对话式AI新体验
Amazon Bedrock Converse API:开启对话式AI新体验 前言 在当今人工智能飞速发展的时代,对话式AI已成为众多应用的核心组成部分。从智能客服到智能助手,对话式AI为用户带来了便捷且高效的交互体验。而Amazon Bedrock Converse API的出现&…...
软考 系统架构设计师系列知识点之杂项集萃(54)
接前一篇文章:软考 系统架构设计师系列知识点之杂项集萃(53) 第87题 某银行系统采用Factory Method方法描述其不同账户之间的关系,设计出的类图如下所示。其中与Factory Method的“Creator”角色对应的类是(ÿ…...
第三章 - 软件质量工程体系
1 概述 系统工程学的思想 系统工程学是为了研究多个子系统构成的整体系统所具有的多种不同目标的相互协调,以期系统功能的最优化、最大限度地发挥系统组成部分的能力而发展起来的一门科学。 软件质量工程体系的建立 建立和实施质量管理体系的方法 确定顾客和其…...
#基础Machine Learning 算法(上)
机器学习算法的分类 机器学习算法大致可以分为三类: 监督学习算法 (Supervised Algorithms):在监督学习训练过程中,可以由训练数据集学到或建立一个模式(函数 / learning model),并依此模式推测新的实例。…...
【YOLO11改进】改进Conv、颈部网络STFEN、以及引入PIOU用于小目标检测!
改进后的整体网络架构 改进一:RFD模块(Conv) YOLOv11模型的跨步卷积下采样虽然快速聚合了局部特征,并且实现了较高的计算效率,但其固有的信息压缩机制会导致细粒度特征的不可逆丢失。针对特征保留与计算效率的平衡问题,本文采用RFD模块替换跨步卷积下采样模块。RFD模块通…...
算法之上的权力——空域治理的政治哲学
当AI算法成为空域资源分配的核心机制,我们不得不直面一个核心问题:谁拥有算法,谁控制算法,谁审查算法?调度系统表面上是中立技术,实则承载了深刻的价值判断与权力结构。本章提出“算法即治理”命题…...
虚幻引擎作者采访
1万小时编程_哔哩哔哩_bilibili https://www.youtube.com/watch?v477qF6QNSvc 提姆斯温尼是一位传奇性的视频游戏程序员,Epic Games 的创始人兼首席执行官。 该公司开发了虚幻引擎、堡垒之夜、战争机器、虚幻竞技场等许多开创性和有影响力的视频游戏。 他哥哥…...
CodeBlocks25配置wxWidgets3.2
一、下载CodeBlocks 25.03版本 1.去Sourceforge.net Code::Blocks - Browse /Binaries/25.03/Windows at SourceForge.net 下载codeblocks-25.03mingw-nosetup.zip 2.解压到d盘,并把目录改为codeblocks 二、.下载wxWidgets 3.2.8 1. 去Sourceforge.net wxWidg…...
Python 整理3种查看神经网络结构的方法
1. 网络结构代码 import torch import torch.nn as nn# 定义Actor-Critic模型 class ActorCritic(nn.Module):def __init__(self, state_dim, action_dim):super(ActorCritic, self).__init__()self.actor nn.Sequential(# 全连接层,输入维度为 state_dim…...
【Bootstrap V4系列】学习入门教程之 组件-卡片(Card)
Bootstrap V4系列 学习入门教程之 组件-卡片(Card) 卡片(Card)一、Example二、Content types 内容类型2.1 Body 主体2.2 Titles, text, and links 标题、文本和链接2.3 Images 图片2.4 List groups 列表组2.5 Kitchen sink 洗涤槽…...
AI Agent开发第50课-机器学习的基础-线性回归如何应用在商业场景中
开篇 虽然我们这个系列主讲AI Agent,但是这个系列是一个喂饭式从0到深入的全AI类计算机教程系列,它主要面向的是培养出一个个AI时代的程序员,不是像外部那种很水的只是做做套壳、聊天、绘图小工具的急功近利式教学。而机器学习是现代AI的基础与基石,一些机器学习、深度学习…...
代码随想录第34天:动态规划7(打家劫舍问题:链式、环式、树式房屋)
一、背包问题小结 1.递推公式: 1.问能否能装满背包(或者最多装多少):dp[j] max(dp[j], dp[j - nums[i]] nums[i]) 2.问装满背包有几种方法:dp[j] dp[j - nums[i]] 3.问背包装满最大价值:dp[j] max…...
网络安全自动化:找准边界才能筑牢安全防线
数字时代,企业每天要面对成千上万的网络攻击。面对庞大的服务器群、分散的团队和长期不重启的设备,很多企业开始思考:哪些安全操作适合交给机器自动处理?哪些必须由人工把关?今天我们就用大白话聊聊这件事。 一、这些事…...
ctfshow——web入门361~368
最近练习ssti 当 Web 应用程序使用模板引擎动态生成 HTML 页面或其他类型的输出时,如果用户输入未经过充分验证或转义就被直接嵌入到模板中,就可能发生 SSTI 攻击。攻击者可以利用这个弱点注入恶意模板代码,该代码将在服务器端执行。 常见的…...
备忘录模式(Memento Pattern)
🧠 备忘录模式(Memento Pattern) 备忘录模式 是行为型设计模式之一。它通过将对象的状态存储在一个备忘录中,允许对象在不暴露其内部结构的情况下,保存和恢复自己的状态。该模式允许将对象的状态保存到备忘录中&#…...
五一假期作业
sub_process.c #include <stdio.h> // 标准输入输出库 #include <pthread.h> // POSIX线程库 #include <sys/ipc.h> // IPC基础定义(如消息队列/共享内存) #include <sys/msg.h> // 消息队列操作相关…...
Multi Agents Collaboration OS:专属多智能体构建—基于业务场景流程构建专属多智能体
背景 随着人工智能技术的飞速发展,大型语言模型(LLM)的能力不断突破,单一智能体的能力边界逐渐显现。为了应对日益复杂的现实世界任务,由多个具备不同能力、可以相互协作的智能体组成的多智能体系统 (Multi-Agent Sys…...
数据库的二级索引
二级索引 10.1 二级索引作为额外的键 表结构 正如第8章提到的,二级索引本质上是包含主键的额外键值对。每个索引通过B树中的键前缀来区分。 type TableDef struct {// 用户定义的部分Name stringTypes []uint32 // 列类型Cols []string // 列名Indexes …...
湖北理元理律师事务所:债务法律服务的民生价值重构
当前我国居民杠杆率达62.3%(央行2023年数据),债务问题已从经济议题演变为社会议题。湖北理元理律师事务所通过构建覆盖咨询、备案、规划的全链条服务,试图在法律框架内探索债务危机的社会化解决方案。 民生导向的服务设计 1.阶梯…...
DotNetBrowser 3.2.0 版本发布啦!
包含来自 Chromium 135 的安全修复支持自定义用户代理客户端提示(User Agent Client Hints)在 Avalonia 离屏渲染模式中支持拖放(Drag & Drop)功能 🔗 点击此处了解更多详情。 🆓 免费试用 30 天。...
PyTorch 张量与自动微分操作
笔记 1 张量索引操作 import torch # 下标从左到右从0开始(0->第一个值), 从右到左从-1开始 # data[行下标, 列下标] # data[0轴下标, 1轴下标, 2轴下标] def dm01():# 创建张量torch.manual_seed(0)data torch.randint(low0, high10, size(4, 5))print(data->,…...
C语言数据在内存中的存储详解
在 C 语言的编程世界里,理解数据在内存中的存储方式是非常重要的,它能帮助我们更好地掌握数据类型、内存管理和程序性能优化等内容。今天,我就来给大家详细讲解数据在内存中的存储,包括整数、大小端字节序和浮点数的存储方式&…...
【AI大模型】SpringBoot整合Spring AI 核心组件使用详解
目录 一、前言 二、Spring AI介绍 2.1 Spring AI介绍 2.2 Spring AI主要特点 2.3 Spring AI核心组件 2.4 Spring AI应用场景 2.5 Spring AI优势 2.5.1 与 Spring 生态无缝集成 2.5.2 模块化设计 2.5.3 简化 AI 集成 2.5.4 支持云原生和分布式计算 2.5.5 安全性保障…...
linux-文件操作
在 Linux 系统中,文件操作与管理是日常使用和系统管理的重要组成部分。下面将详细介绍文件的复制、移动、链接创建,以及文件查找、文本处理、排序、权限管理等相关知识。 一、文件的复制 在 Linux 里,cp 命令可用于复制文件或目录ÿ…...
丢失的数字 --- 位运算
目录 一:题目 二:算法原理 三:代码实现 一:题目 题目链接: 268. 丢失的数字 - 力扣(LeetCode) 二:算法原理 三:代码实现 class Solution { public:int missingNumb…...