NumPy 超详细教程(3):ndarray 的内部机理及高级迭代

系列文章地址


ndarray 对象的内部机理

在前面的内容中,我们已经详细讲述了 ndarray 的使用,在本章的开始部分,我们来聊一聊 ndarray 的内部机理,以便更好的理解后续的内容。

1、ndarray 的组成

ndarray 与数组不同,它不仅仅包含数据信息,还包括其他描述信息。ndarray 内部由以下内容组成:

  • 数据指针:一个指向实际数据的指针。
  • 数据类型(dtype):描述了每个元素所占字节数。
  • 维度(shape):一个表示数组形状的元组。
  • 跨度(strides):一个表示从当前维度前进道下一维度的当前位置所需要“跨过”的字节数。

NumPy 中,数据存储在一个均匀连续的内存块中,可以这么理解,NumPy 将多维数组在内部以一维数组的方式存储,我们只要知道了每个元素所占的字节数(dtype)以及每个维度中元素的个数(shape),就可以快速定位到任意维度的任意一个元素。

dtypeshape 前文中已经有详细描述,这里我们来讲下 strides

示例

ls = [[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],       [[13, 14, 15, 16], [17, 18, 19, 20], [21, 22, 23, 24]]]a = np.array(ls, dtype=int)print(a)print(a.strides)

输出:

[[[ 1  2  3  4]  [ 5  6  7  8]  [ 9 10 11 12]]

 [[13 14 15 16]  [17 18 19 20]  [21 22 23 24]]](48, 16, 4)

上例中,我们定义了一个三维数组,dtypeintint 占 4个字节。
第一维度,从元素 1 到元素 13,间隔 12 个元素,总字节数为 48;
第二维度,从元素 1 到元素 5,间隔 4 个元素,总字节数为 16;
第三维度,从元素 1 到元素 2,间隔 1 个元素,总字节数为 4。
所以跨度为(48, 16, 4)。

普通迭代

ndarray 的普通迭代跟 Python 及其他语言中的迭代方式无异,N 维数组,就要用 N 层的 for 循环。

示例:

import numpy as np

ls = [[1, 2], [3, 4], [5, 6]]a = np.array(ls, dtype=int)for row in a:    for cell in row:        print(cell)

输出:

123456

上例中,row 的数据类型依然是 numpy.ndarray,而 cell 的数据类型是 numpy.int32

nditer 多维迭代器

NumPy 提供了一个高效的多维迭代器对象:nditer 用于迭代数组。在普通方式的迭代中,N 维数组,就要用 N 层的 for 循环。但是使用 nditer 迭代器,一个 for 循环就能遍历整个数组。(因为 ndarray 在内存中是连续的,连续内存不就相当于是一维数组吗?遍历一维数组当然只需要一个 for 循环就行了。)

1、基本示例

例一:

ls = [[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],      [[13, 14, 15, 16], [17, 18, 19, 20], [21, 22, 23, 24]]]a = np.array(ls, dtype=int)for x in np.nditer(a):    print(x, end=", ")

输出:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 

2、order 参数:指定访问元素的顺序

创建 ndarray 数组时,可以通过 order 参数指定元素的顺序,按行还是按列,这是什么意思呢?来看下面的示例:

例二:

ls = [[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],      [[13, 14, 15, 16], [17, 18, 19, 20], [21, 22, 23, 24]]]a = np.array(ls, dtype=int, order=‘F‘)for x in np.nditer(a):    print(x, end=", ")

输出:

1, 13, 5, 17, 9, 21, 2, 14, 6, 18, 10, 22, 3, 15, 7, 19, 11, 23, 4, 16, 8, 20, 12, 24, 

nditer 默认以内存中元素的顺序(order=‘K‘)访问元素,对比例一可见,创建 ndarray 时,指定不同的顺序将影响元素在内存中的位置。

例三:
nditer 也可以指定使用某种顺序遍历。

ls = [[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],      [[13, 14, 15, 16], [17, 18, 19, 20], [21, 22, 23, 24]]]a = np.array(ls, dtype=int, order=‘F‘)for x in np.nditer(a, order=‘C‘):    print(x, end=", ")

输出:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 

行主顺序(order=‘C‘)和列主顺序(order=‘F‘),参看 https://en.wikipedia.org/wiki/Row-_and_column-major_order。例一是行主顺序,例二是列主顺序,如果将 ndarray 数组想象成一棵树,那么会发现,行主顺序就是深度优先,而列主顺序就是广度优先。NumPy 中之所以要分行主顺序和列主顺序,主要是为了在矩阵运算中提高性能,顺序访问比非顺序访问快几个数量级。(矩阵运算将会在后面的章节中讲到)

3、op_flags 参数:迭代时修改元素的值

默认情况下,nditer 将视待迭代遍历的数组为只读对象(readonly),为了在遍历数组的同时,实现对数组元素值得修改,必须指定 op_flags 参数为 readwrite 或者 writeonly 的模式。

例四:

import numpy as np

a = np.arange(5)for x in np.nditer(a, op_flags=[‘readwrite‘]):    x[...] = 2 * xprint(a)

输出:

[0 1 2 3 4]

4、flags 参数

flags 参数需要传入一个数组或元组,既然参数类型是数组,我原本以为可以传入多个值的,但是,就下面介绍的 4 种常用选项,我试了,不能传多个,例如 flags=[‘f_index‘, ‘external_loop‘],运行报错

(1)使用外部循环:external_loop

将一维的最内层的循环转移到外部循环迭代器,使得 NumPy 的矢量化操作在处理更大规模数据时变得更有效率。

简单来说,当指定 flags=[‘external_loop‘] 时,将返回一维数组而并非单个元素。具体来说,当 ndarray 的顺序和遍历的顺序一致时,将所有元素组成一个一维数组返回;当 ndarray 的顺序和遍历的顺序不一致时,返回每次遍历的一维数组(这句话特别不好描述,看例子就清楚了)。

例五:

import numpy as np

ls = [[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],      [[13, 14, 15, 16], [17, 18, 19, 20], [21, 22, 23, 24]]]a = np.array(ls, dtype=int, order=‘C‘)for x in np.nditer(a, flags=[‘external_loop‘], order=‘C‘):    print(x,)

输出:

[ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24]

例六:

b = np.array(ls, dtype=int, order=‘F‘)for x in np.nditer(b, flags=[‘external_loop‘], order=‘C‘):    print(x,)

输出:

[1 2 3 4][5 6 7 8][ 9 10 11 12][13 14 15 16][17 18 19 20][21 22 23 24]

(2)追踪索引:c_index、f_index、multi_index

例七:

import numpy as np

a = np.arange(6).reshape(2, 3)it = np.nditer(a, flags=[‘f_index‘])

while not it.finished:    print("%d <%d>" % (it[0], it.index))    it.iternext()

输出:

0 <0>1 <2>2 <4>3 <1>4 <3>5 <5>

这里索引之所以是这样的顺序,因为我们选择的是列索引(f_index)。直观的感受看下图:

遍历元素的顺序是由 order 参数决定的,而行索引(c_index)和列索引(f_index)不论如何指定,并不会影响元素返回的顺序。它们仅表示在当前内存顺序下,如果按行/列顺序返回,各个元素的下标应该是多少。

例八:

import numpy as np

a = np.arange(6).reshape(2, 3)it = np.nditer(a, flags=[‘multi_index‘])

while not it.finished:    print("%d <%s>" % (it[0], it.multi_index))    it.iternext()

输出:

0 <(0, 0)>1 <(0, 1)>2 <(0, 2)>3 <(1, 0)>4 <(1, 1)>5 <(1, 2)>

5、同时迭代多个数组

说到同时遍历多个数组,第一反应会想到 zip 函数,而在 nditer 中不需要。

例九:

a = np.array([1, 2, 3], dtype=int, order=‘C‘)b = np.array([11, 12, 13], dtype=int, order=‘C‘)for x, y in np.nditer([a, b]):    print(x, y)

输出:

1 112 123 13

其他函数

1、flatten函数

flatten 函数将多维 ndarray 展开成一维 ndarray 返回。
语法:

flatten(order=‘C‘)

示例:

import numpy as np

a = np.array([[1, 2, 3], [4, 5, 6]], dtype=int, order=‘C‘)b = a.flatten()print(b)print(type(b))

输出:

[1 2 3 4 5 6]<class ‘numpy.ndarray‘>

2、flat

flat 返回一个迭代器,可以遍历数组中的每一个元素。

import numpy as np

a = np.array([[1, 2, 3], [4, 5, 6]], dtype=int, order=‘C‘)for b in a.flat:    print(b)print(type(a.flat))

输出:

123456<class ‘numpy.flatiter‘>

欢迎关注我的公众号大龄码农的Python之路

原文地址:https://www.cnblogs.com/gl1573/p/10634857.html

时间: 2024-10-29 19:07:54

NumPy 超详细教程(3):ndarray 的内部机理及高级迭代的相关文章

NumPy的详细教程

NumPy的详细教程 转载 http://blog.csdn.net/chen_shiqiang/article/details/51868115 先决条件 在阅读这个教程之前,你多少需要知道点python.如果你想从新回忆下,请看看 Python Tutorial . 如果你想要运行教程中的示例,你至少需要在你的电脑上安装了以下一些软件: Python NumPy 这些是可能对你有帮助的: ipython 是一个净强化的交互Python Shell,对探索NumPy的特性非常方便. matpl

【数据结构】10分钟教你用栈求解迷宫老鼠问题超详细教程附C++源代码

问题描述 给定一张迷宫地图和一个迷宫入口,然后进入迷宫探索找到一个出口.如下图所示: 该图是一个矩形区域,有一个入口和出口.迷宫内部包含不能穿越的墙壁或者障碍物.这些障碍物沿着行和列放置,与迷宫的边界平行.迷宫的入口在左上角,出口在右下角. 问题分析 首先要有一张迷宫地图,地图由两部分组成: (1)一是迷宫中各处的位置坐标, (2)二是迷宫各位置处的状态信息,即该处是墙还是路 所以,该迷宫地图可由一个二维数组来表示.数组的横纵坐标表示迷宫各处的位置坐标,数组元素表示各位置处的状态信息. 2.在这

Nginx反代超详细教程:加速网站Google、Gravatar和Hostloc

VPS教程 » Nginx反代超详细教程:加速网站Google.Gravatar和Hostloc Nginx反代超详细教程:加速网站Google.Gravatar和Hostloc December 31st , 2015•Edit•访问: 672 次 nginx 这个轻量级.高性能的 web server 主要可以干两件事情: 1.直接作为http server(需要Fastcgi配合): 2.作为反代服务器(进一步可以实现均衡负载). 这里主要利用一下反功能来方便一下日常生活.选择Gravat

安装64位Oracle 10g超详细教程

安装64位Oracle 10g超详细教程 1. 安装准备阶段 1.1 安装Oracle环境 经过上一篇博文的过程,已经完成了对Linux系统的安装,本例使用X-Manager来实现与Linux系统的连接,本例使用的所有命令和操作都是在X-Manager下进行.X-Manager安装完成后的配置方法如下: 1.  打开X-Manager的X-Shell 2. 点击New,新建一个连接地址,设置完成后,点击OK3.  使用用户名,密码进行登录,登录完成后,进入如下图所示画面即成功连接到Linux系统

Ubuntu-安装-cuda7.0-单显卡-超详细教程

欢迎访问 博客新址 一.说明 本教程是在台式机上安装的,只有一个NVIDIA显卡. 操作系统是Ubuntu 14.04 (64bit). 双显卡的笔记本请移步Ubuntu-安装-cuda7.0-双显卡-超详细教程 二.准备 说明:本文假设下载的文件都在~/Dowloads/下面 1. 更新操作系统 sudo apt-get update 2. 下载cuda7.0 点此下载 如果不是该版本,可以搜索,如下图所示: 点击"Linux x86",选择"Ubuntu 14.04&qu

VMware10.0安装Mac OS X 10.9超详细教程

最新版的VMware10.0支持中文,无需汉化,安装即可:不过还是需要注册码,注册机是必须有的请放心,下载地址: 点击进入 其它所需软件: 1.系统:用的是论坛里网友做的懒人版是.cdr文件(真接把.cdr改为.iso就是镜像文件了)  点击进入 (也可以去下原版镜像dmg文件,有7z打开提取里面的InstallESD.dmg,然后用UltraISO转化为ISO镜像文件也可以安装,本人亲试成功) 2.mac补丁unlock-all-v120.zip  点击进入 3.VMware Tools da

Github上传代码菜鸟超详细教程【转】

最近需要将课设代码上传到Github上,之前只是用来fork别人的代码. 这篇文章写得是windows下的使用方法. 第一步:创建Github新账户 第二步:新建仓库 第三部:填写名称,简介(可选),勾选Initialize this repository with a README选项,这是自动创建REAMDE.md文件,省的你再创建. 第四步:安装Github shell程序,地址:http://windows.github.com/ 第五步:打开Git Shell,输入以下命令生成密钥来验

10分钟教你用python打造贪吃蛇超详细教程

10分钟教你用python打造贪吃蛇超详细教程 在家闲着没妹子约, 刚好最近又学了一下python,听说pygame挺好玩的.今天就在家研究一下, 弄了个贪吃蛇出来.希望大家喜欢. 先看程序效果: 01 整体框架 平台:pycharm 关于pygame的安装这里就不在赘述,大家自行上网找合适自己的版本的安装即可.关于pygame模块知识会穿插在下面代码中介绍,用到什么就介绍什么.这里就不统一介绍了. 整个程序由于是调用了大量的pygame里面的库函数,所以也非常简单(卧槽你这不是调包侠嘛).也就

【超详细教程】使用Windows Live Writer 2012和Office Word 2013 发布

去年就知道有这个功能,不过没去深究总结过,最近有写网络博客的欲望了,于是又重新拾起这玩意儿. 具体到底是用Windows Live Writer 2012还是用Word 2013,个人觉得看个人,因为这2个软件各有优点,各有缺点. 1.首先用LiveWriter发博客显然更专业,发布后的效果也与本地最接近,但是在编辑功能上肯定大不如Word,另外一个最大缺点是它本地保存的格式不是doc. 2.而Word的优点不言而喻,我们天天用Word,用Word就能发博客这简直就是一个天大的好消息,但用Wor