wordpress公园,北京网站优化推广收集,网站建设广找金手指排名贰肆,怎么制作网站logo1.2 《ndarray解剖课#xff1a;多维数组的底层实现》
内容介绍
NumPy 的 ndarray 是其核心数据结构#xff0c;用于高效处理多维数组。在这篇文章中#xff0c;我们将深入解析 ndarray 的底层实现#xff0c;探讨其内存结构、维度、数据类型、步长等关键概念#xff0c…
1.2 《ndarray解剖课多维数组的底层实现》
内容介绍
NumPy 的 ndarray 是其核心数据结构用于高效处理多维数组。在这篇文章中我们将深入解析 ndarray 的底层实现探讨其内存结构、维度、数据类型、步长等关键概念并通过实验验证这些概念的实际应用。
1.2.1 ndarray与Python列表的核心差异
ndarray 和 Python 列表是两种不同的数据结构它们在内存布局和性能上有显著的差异。下面是 ndarray 和 Python 列表的核心差异对比表
特性ndarrayPython 列表内存布局连续的内存块固定大小动态分配的内存指向对象的指针数据类型统一的数据类型dtype混合的数据类型可以包含任意类型的对象访问速度高效的向量化操作较慢的迭代访问修改成本低视图和副本高需要重新分配内存支持的运算广泛的数学和科学计算功能有限的列表操作数据对齐自动对齐通过步长无对齐计算性能高利用C/C实现低纯Python实现文件读写支持 .npy 和 .npz 文件格式不支持二进制文件格式需要额外的库支持集成性与 Pandas、Scikit-learn 等科学计算库高度集成与标准库高度集成但与其他科学计算库集成度较低
1.2.2 ndarray内存结构3D示意图
为了更好地理解 ndarray 的内存结构我们绘制一个 3D 示意图展示 ndarray 如何在内存中存储多维数组。 #mermaid-svg-LWu0qdvKAt0u8wco {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-LWu0qdvKAt0u8wco .error-icon{fill:#552222;}#mermaid-svg-LWu0qdvKAt0u8wco .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LWu0qdvKAt0u8wco .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-LWu0qdvKAt0u8wco .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LWu0qdvKAt0u8wco .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LWu0qdvKAt0u8wco .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LWu0qdvKAt0u8wco .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LWu0qdvKAt0u8wco .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LWu0qdvKAt0u8wco .marker.cross{stroke:#333333;}#mermaid-svg-LWu0qdvKAt0u8wco svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LWu0qdvKAt0u8wco .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LWu0qdvKAt0u8wco .cluster-label text{fill:#333;}#mermaid-svg-LWu0qdvKAt0u8wco .cluster-label span{color:#333;}#mermaid-svg-LWu0qdvKAt0u8wco .label text,#mermaid-svg-LWu0qdvKAt0u8wco span{fill:#333;color:#333;}#mermaid-svg-LWu0qdvKAt0u8wco .node rect,#mermaid-svg-LWu0qdvKAt0u8wco .node circle,#mermaid-svg-LWu0qdvKAt0u8wco .node ellipse,#mermaid-svg-LWu0qdvKAt0u8wco .node polygon,#mermaid-svg-LWu0qdvKAt0u8wco .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LWu0qdvKAt0u8wco .node .label{text-align:center;}#mermaid-svg-LWu0qdvKAt0u8wco .node.clickable{cursor:pointer;}#mermaid-svg-LWu0qdvKAt0u8wco .arrowheadPath{fill:#333333;}#mermaid-svg-LWu0qdvKAt0u8wco .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LWu0qdvKAt0u8wco .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LWu0qdvKAt0u8wco .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-LWu0qdvKAt0u8wco .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-LWu0qdvKAt0u8wco .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LWu0qdvKAt0u8wco .cluster text{fill:#333;}#mermaid-svg-LWu0qdvKAt0u8wco .cluster span{color:#333;}#mermaid-svg-LWu0qdvKAt0u8wco div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LWu0qdvKAt0u8wco :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 元数据 内存块 3, 3 维度 (shape) 8 (64-bit float) 数据类型 (dtype) (24, 8) 步长 (strides) 元素1 数据 元素2 元素3 元素4 元素5 元素6 元素7 元素8 元素9 NumPy ndarray 内存块 元数据 内存布局示意图三维数组示例
内存地址0x10000x10040x10080x100C0x10100x1014…三维索引[0,0,0][0,0,1][0,1,0][0,1,1][1,0,0][1,0,1]…二维展开[0,0][0,1][1,0][1,1][2,0][2,1]…一维展开012345…
内存布局验证实验
import numpy as np# 创建基础数组
base_arr np.arange(6, dtypenp.int32)
print(f原始数组ID: {id(base_arr)}) # 输出原始数组内存地址# 创建视图
view_arr base_arr[::2] # 步长切片创建视图
print(f视图数组ID: {id(view_arr)}) # 地址不同但共享数据# 创建副本
copy_arr base_arr.copy() # 完整内存复制
print(f副本数组ID: {id(copy_arr)}) # 全新内存地址# 修改视图影响原始数组
view_arr[0] 100
print(修改视图后的原始数组:, base_arr) # 输出[100 1 2 3 4 5]1.2.3 维度(shape)、数据类型(dtype)、步长(strides)的关联关系
ndarray 的三个关键属性是 shape维度、dtype数据类型和 strides步长。它们之间的关系如下
shape表示数组的形状即每个维度的大小。例如shape(3, 3) 表示一个 3x3 的二维数组。dtype表示数组中每个元素的数据类型。例如dtypenp.float64 表示数组中的元素是 64 位浮点数。strides表示在内存中从一个元素移动到下一个元素所需的字节数。例如在一个 shape(3, 3)、dtypenp.float64 的数组中步长 strides(24, 8) 表示从一个行到下一个行需要移动 24 个字节从一个列到下一个列需要移动 8 个字节。
步长计算公式推导
对于形状为 ( d 1 , d 2 , . . . , d n ) (d_1,d_2,...,d_n) (d1,d2,...,dn)的数组第 k k k维步长 s t r i d e k ( ∏ i k 1 n d i ) × i t e m s i z e stride_k \left( \prod_{ik1}^{n} d_i \right) \times itemsize stridek(ik1∏ndi)×itemsize
示例三维数组(2,3,4)数据类型int324字节
axis0_stride 3*4*4 48 字节
axis1_stride 4*4 16 字节
axis2_stride 4 字节1.2.4 不同初始化方式的内存分配对比zeros vs empty
NumPy 提供了多种初始化数组的方法其中 np.zeros 和 np.empty 是两个常用的方法。我们将通过实验对比它们的内存分配方式。
import numpy as np# 创建一个 3x3 的零数组
zeros_array np.zeros((3, 3), dtypenp.float64)
print(零数组:)
print(zeros_array)# 创建一个 3x3 的未初始化数组
empty_array np.empty((3, 3), dtypenp.float64)
print(未初始化数组:)
print(empty_array)# 验证两个数组的内存地址
print(零数组的内存地址:, id(zeros_array))
print(未初始化数组的内存地址:, id(empty_array))# 验证两个数组的相同元素是否共享内存
a zeros_array[0, 0]
b empty_array[0, 0]
print(零数组的首元素内存地址:, id(a))
print(未初始化数组的首元素内存地址:, id(b))注释
# 导入 NumPy 库并将其别名为 np
import numpy as np# 创建一个 3x3 的零数组
# np.zeros 是 NumPy 中用于创建全零数组的函数
# 传入数组的形状和数据类型作为参数
zeros_array np.zeros((3, 3), dtypenp.float64)
print(零数组:) # 打印零数组
print(zeros_array)# 创建一个 3x3 的未初始化数组
# np.empty 是 NumPy 中用于创建未初始化数组的函数
# 传入数组的形状和数据类型作为参数
empty_array np.empty((3, 3), dtypenp.float64)
print(未初始化数组:) # 打印未初始化数组
print(empty_array)# 验证两个数组的内存地址
# id() 函数用于获取对象的内存地址
print(零数组的内存地址:, id(zeros_array))
print(未初始化数组的内存地址:, id(empty_array))# 验证两个数组的相同元素是否共享内存
# 获取零数组和未初始化数组的首元素
a zeros_array[0, 0]
b empty_array[0, 0]
print(零数组的首元素内存地址:, id(a))
print(未初始化数组的首元素内存地址:, id(b))1.2.5 数组元属性操作实验shape修改的边界条件
ndarray 的 shape 属性可以动态修改但有一些边界条件需要遵守。我们将通过实验验证这些边界条件。
import numpy as np# 创建一个 3x3 的数组
array np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtypenp.float64)
print(原始数组:)
print(array)# 修改数组的形状为 1x9
array.shape (1, 9)
print(修改后的数组1x9:)
print(array)# 修改数组的形状为 9x1
array.shape (9, 1)
print(修改后的数组9x1:)
print(array)# 尝试修改数组的形状为 4x3
try:array.shape (4, 3)
except ValueError as e:print(尝试修改形状为 4x3 时的错误:, e)# 尝试修改数组的形状为 3x3x3
try:array.shape (3, 3, 3)
except ValueError as e:print(尝试修改形状为 3x3x3 时的错误:, e)# 修改数组的形状为 3x3
array.shape (3, 3)
print(恢复数组形状为 3x3:)
print(array)注释
# 导入 NumPy 库并将其别名为 np
import numpy as np# 创建一个 3x3 的数组
# np.array 是 NumPy 中用于创建数组的函数
# 传入二维列表每个子列表代表数组的一行指定数据类型为 64 位浮点数
array np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtypenp.float64)
print(原始数组:) # 打印原始数组
print(array)# 修改数组的形状为 1x9
# .shape 属性用于获取或设置数组的形状
array.shape (1, 9)
print(修改后的数组1x9:) # 打印修改后的数组
print(array)# 修改数组的形状为 9x1
array.shape (9, 1)
print(修改后的数组9x1:) # 打印修改后的数组
print(array)# 尝试修改数组的形状为 4x3
# 这将导致 ValueError因为数组的总元素数9不等于目标形状的总元素数12
try:array.shape (4, 3)
except ValueError as e:print(尝试修改形状为 4x3 时的错误:, e)# 尝试修改数组的形状为 3x3x3
# 这将导致 ValueError因为数组的总元素数9不等于目标形状的总元素数27
try:array.shape (3, 3, 3)
except ValueError as e:print(尝试修改形状为 3x3x3 时的错误:, e)# 修改数组的形状为 3x3
# 成功修改回原形状
array.shape (3, 3)
print(恢复数组形状为 3x3:) # 打印恢复后的数组
print(array)总结
通过这篇文章我们深入解析了 NumPy 的 ndarray 的底层实现探讨了其内存结构、维度、数据类型、步长等关键概念并通过实验验证了这些概念的实际应用。希望这些内容能帮助你更好地理解和使用 NumPy。
参考文献或资料
参考资料名称链接NumPy 官方文档https://numpy.org/doc/Python 官方文档https://docs.python.org/3/NumPy 入门指南https://numpy.org/devdocs/user/quickstart.htmlNumPy 源码分析https://github.com/numpy/numpyNumPy 速查表https://www.kaggle.com/learn/overviewNumPy 实战案例https://www.tensorflow.org/tutorials/quickstart/beginnerNumPy 书籍推荐https://www.springer.com/gp/book/9781484242452NumPy 视频教程https://www.youtube.com/watch?vQUT1VHiLmmINumPy 交互式学习https://colab.research.google.com/Python 内存管理https://docs.python.org/3/c-api/memory.htmlC 语言内存管理https://en.wikipedia.org/wiki/C_memory_allocation数据结构与算法https://www.geeksforgeeks.org/深度学习中的数组操作https://pytorch.org/tutorials/beginner/basics/tensorqs_tutorial.html科学计算库对比https://www.tensorflow.org/compare高效计算技术https://en.wikipedia.org/wiki/High-performance_computing编程社区讨论https://stackoverflow.com/questions/tagged/numpy
希望这篇文章能帮助你在 NumPy 的学习和使用中更进一步。这篇文章包含了详细的原理介绍、代码示例、源码注释以及案例等。希望这对您有帮助。如果有任何问题请随私信或评论告诉我。