forked from apachecn/numpy-doc-zh
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathinternals.html
77 lines (75 loc) · 15.6 KB
/
internals.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<h1><span class="yiyi-st" id="yiyi-14">NumPy internals</span></h1>
<blockquote>
<p>原文:<a href="https://docs.scipy.org/doc/numpy/reference/internals.html">https://docs.scipy.org/doc/numpy/reference/internals.html</a></p>
<p>译者:<a href="https://github.com/wizardforcel">飞龙</a> <a href="http://usyiyi.cn/">UsyiyiCN</a></p>
<p>校对:(虚位以待)</p>
</blockquote>
<div class="toctree-wrapper compound">
<ul>
<li class="toctree-l1"><span class="yiyi-st" id="yiyi-34"><a class="reference internal" href="internals.code-explanations.html">NumPy C代码说明</a></span><ul>
<li class="toctree-l2"><span class="yiyi-st" id="yiyi-15"><a class="reference internal" href="internals.code-explanations.html#memory-model">内存模型</a></span></li>
<li class="toctree-l2"><span class="yiyi-st" id="yiyi-16"><a class="reference internal" href="internals.code-explanations.html#data-type-encapsulation">数据类型封装</a></span></li>
<li class="toctree-l2"><span class="yiyi-st" id="yiyi-17"><a class="reference internal" href="internals.code-explanations.html#n-d-iterators">N-D迭代器</a></span></li>
<li class="toctree-l2"><span class="yiyi-st" id="yiyi-18"><a class="reference internal" href="internals.code-explanations.html#broadcasting">广播</a></span></li>
<li class="toctree-l2"><span class="yiyi-st" id="yiyi-19"><a class="reference internal" href="internals.code-explanations.html#array-scalars">数组标量</a></span></li>
<li class="toctree-l2"><span class="yiyi-st" id="yiyi-21"><a class="reference internal" href="internals.code-explanations.html#indexing">索引</a></span><ul>
<li class="toctree-l3"><span class="yiyi-st" id="yiyi-20"><a class="reference internal" href="internals.code-explanations.html#advanced-indexing">高级索引</a></span></li>
</ul>
</li>
<li class="toctree-l2"><span class="yiyi-st" id="yiyi-33"><a class="reference internal" href="internals.code-explanations.html#universal-functions">通用功能</a></span><ul>
<li class="toctree-l3"><span class="yiyi-st" id="yiyi-22"><a class="reference internal" href="internals.code-explanations.html#setup">设置</a></span></li>
<li class="toctree-l3"><span class="yiyi-st" id="yiyi-26"><a class="reference internal" href="internals.code-explanations.html#function-call">函数调用</a></span><ul>
<li class="toctree-l4"><span class="yiyi-st" id="yiyi-23"><a class="reference internal" href="internals.code-explanations.html#one-loop">单循环</a></span></li>
<li class="toctree-l4"><span class="yiyi-st" id="yiyi-24"><a class="reference internal" href="internals.code-explanations.html#strided-loop">串联回路</a></span></li>
<li class="toctree-l4"><span class="yiyi-st" id="yiyi-25"><a class="reference internal" href="internals.code-explanations.html#buffered-loop">缓冲循环</a></span></li>
</ul>
</li>
<li class="toctree-l3"><span class="yiyi-st" id="yiyi-27"><a class="reference internal" href="internals.code-explanations.html#final-output-manipulation">最终输出操作</a></span></li>
<li class="toctree-l3"><span class="yiyi-st" id="yiyi-32"><a class="reference internal" href="internals.code-explanations.html#methods">方法</a></span><ul>
<li class="toctree-l4"><span class="yiyi-st" id="yiyi-28"><a class="reference internal" href="internals.code-explanations.html#id1">设置</a></span></li>
<li class="toctree-l4"><span class="yiyi-st" id="yiyi-29"><a class="reference internal" href="internals.code-explanations.html#reduce">缩小</a></span></li>
<li class="toctree-l4"><span class="yiyi-st" id="yiyi-30"><a class="reference internal" href="internals.code-explanations.html#accumulate">累计</a></span></li>
<li class="toctree-l4"><span class="yiyi-st" id="yiyi-31"><a class="reference internal" href="internals.code-explanations.html#reduceat">Reduceat</a></span></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<span class="target" id="module-numpy.doc.internals"></span><div class="section" id="internal-organization-of-numpy-arrays">
<h2><span class="yiyi-st" id="yiyi-35">Internal organization of numpy arrays</span></h2>
<p><span class="yiyi-st" id="yiyi-36">它有助于理解一点关于如何处理numpy数组在封面下有助于更好地理解numpy。</span><span class="yiyi-st" id="yiyi-37">本节不会详细介绍。</span><span class="yiyi-st" id="yiyi-38">那些希望了解完整细节的人可参考Travis Oliphant的“Numpy指南”。</span></p>
<p><span class="yiyi-st" id="yiyi-39">Numpy数组由两个主要组件组成,原始数组数据(从现在开始,称为数据缓冲区)和有关原始数组数据的信息。</span><span class="yiyi-st" id="yiyi-40">数据缓冲区通常是人们认为的C或Fortran中的数组,一个包含固定大小数据项的连续(和固定)内存块。</span><span class="yiyi-st" id="yiyi-41">Numpy还包含一组重要的数据,描述如何解释数据缓冲区中的数据。</span><span class="yiyi-st" id="yiyi-42">此额外信息包含(除其他外):</span></p>
<blockquote>
<div><ol class="arabic simple">
<li><span class="yiyi-st" id="yiyi-43">基本数据元素的大小(以字节为单位)</span></li>
<li><span class="yiyi-st" id="yiyi-44">数据缓冲区中的数据的开始(相对于数据缓冲区的开始的偏移)。</span></li>
<li><span class="yiyi-st" id="yiyi-45">维度的数量和每个维度的大小</span></li>
<li><span class="yiyi-st" id="yiyi-46">每个维度的元素之间的距离(“stride”)。</span><span class="yiyi-st" id="yiyi-47">这不必是元素大小的倍数</span></li>
<li><span class="yiyi-st" id="yiyi-48">数据的字节顺序(可能不是原生字节顺序)</span></li>
<li><span class="yiyi-st" id="yiyi-49">缓冲区是否为只读</span></li>
<li><span class="yiyi-st" id="yiyi-50">关于基本数据元素的解释的信息(通过dtype对象)。</span><span class="yiyi-st" id="yiyi-51">基本数据元素可以像int或float一样简单,或者它可以是复合对象(例如,结构体),固定字符字段或Python对象指针。</span></li>
<li><span class="yiyi-st" id="yiyi-52">数组是否被解释为C阶或Fortran阶。</span></li>
</ol>
</div></blockquote>
<p><span class="yiyi-st" id="yiyi-53">这种安排允许非常灵活地使用数组。</span><span class="yiyi-st" id="yiyi-54">它允许的一个事情是元数据的简单更改,以更改数组缓冲区的解释。</span><span class="yiyi-st" id="yiyi-55">更改数组的字节顺序是一个简单的更改,不涉及重新排列数据。</span><span class="yiyi-st" id="yiyi-56">数组的形状可以非常容易地改变,而不改变数据缓冲器中的任何东西或根本不改变任何数据复制</span></p>
<p><span class="yiyi-st" id="yiyi-57">除此之外,使得可能的是可以创建新的数组元数据对象,其使用相同的数据缓冲器来创建该数据缓冲器的新视图,其具有缓冲器的不同解释(例如,不同的形状,偏移,字节顺序,步幅等),但共享相同的数据字节。</span><span class="yiyi-st" id="yiyi-58">numpy中的许多操作只是这样,如切片。</span><span class="yiyi-st" id="yiyi-59">其他操作(如转置)不会在数组中移动数据元素,而是更改有关形状和步长的信息,以便数组的索引更改,但数据中的数据不会移动。</span></p>
<p><span class="yiyi-st" id="yiyi-60">通常,这些新版本的数组元数据,但相同的数据缓冲区是新的“视图”到数据缓冲区。</span><span class="yiyi-st" id="yiyi-61">有一个不同的ndarray对象,但它使用相同的数据缓冲区。</span><span class="yiyi-st" id="yiyi-62">这就是为什么有必要通过使用.copy()方法强制复制,如果真的想要一个新的和独立的数据缓冲区的副本。</span></p>
<p><span class="yiyi-st" id="yiyi-63">将新视图转换为数组意味着数据缓冲区的对象引用计数增加。</span><span class="yiyi-st" id="yiyi-64">只要取消原来的数组对象,就不会删除数据缓冲区,如果它的其他视图仍然存在。</span></p>
</div>
<div class="section" id="multidimensional-array-indexing-order-issues">
<h2><span class="yiyi-st" id="yiyi-65">Multidimensional Array Indexing Order Issues</span></h2>
<p><span class="yiyi-st" id="yiyi-66">什么是索引多维数组的正确方法?</span><span class="yiyi-st" id="yiyi-67">在你跳到结束关于一个和真正的方式索引多维数组之前,它理解为什么这是一个混乱的问题。</span><span class="yiyi-st" id="yiyi-68">本节将尝试详细解释numpy索引如何工作,以及为什么我们采用我们为图像做的约定,以及何时采用其他约定。</span></p>
<p><span class="yiyi-st" id="yiyi-69">首先要理解的是,有两种冲突的约定用于索引2维数组。</span><span class="yiyi-st" id="yiyi-70">矩阵符号使用第一个索引来指示正在选择哪一行,第二个索引用于指示选择哪个列。</span><span class="yiyi-st" id="yiyi-71">这与图像的几何定向约定相反,其中人们通常认为第一索引表示x位置(即,列),第二索引表示y位置(即,行)。</span><span class="yiyi-st" id="yiyi-72">这只是造成混乱的根源;面向矩阵的用户和面向图像的用户期望有关索引的两个不同的事情。</span></p>
<p><span class="yiyi-st" id="yiyi-73">第二个要理解的问题是索引如何对应于数组存储在内存中的顺序。</span><span class="yiyi-st" id="yiyi-74">在Fortran中,当移动通过存储在存储器中的二维数组的元素时,第一索引是最快变化的索引。</span><span class="yiyi-st" id="yiyi-75">如果你采用矩阵约定索引,那么这意味着矩阵一次存储一列(因为第一个索引移动到下一行,因为它改变)。</span><span class="yiyi-st" id="yiyi-76">因此,Fortran被认为是一个列主语言。</span><span class="yiyi-st" id="yiyi-77">C只是相反的约定。</span><span class="yiyi-st" id="yiyi-78">在C中,最后的索引随着存储在存储器中的数组移动而变化最快。</span><span class="yiyi-st" id="yiyi-79">因此C是行主语言。</span><span class="yiyi-st" id="yiyi-80">矩阵按行存储。</span><span class="yiyi-st" id="yiyi-81">注意,在这两种情况下,它假定正在使用用于索引的矩阵约定,即对于Fortran和C,第一索引是行。</span><span class="yiyi-st" id="yiyi-82">注意这个约定意味着索引约定是不变的,并且数据顺序改变以保持这样。</span></p>
<p><span class="yiyi-st" id="yiyi-83">但这不是看它的唯一方法。</span><span class="yiyi-st" id="yiyi-84">假设有一个大的二维数组(图像或矩阵)存储在数据文件中。</span><span class="yiyi-st" id="yiyi-85">假设数据是按行存储的,而不是按列存储的。</span><span class="yiyi-st" id="yiyi-86">如果我们要保留索引约定(无论是矩阵还是图像),这意味着根据我们使用的语言,如果数据被读入内存,我们可能被迫重新排序,以保留索引约定。</span><span class="yiyi-st" id="yiyi-87">例如,如果我们在没有重新排序的情况下将行顺序数据读入内存,它将匹配C的矩阵索引约定,但不匹配Fortran。</span><span class="yiyi-st" id="yiyi-88">相反,它将匹配Fortran的映像索引约定,但不匹配C.对于C,如果使用以行顺序存储的数据,并且想要保留图像索引约定,则在读取存储器时必须重新排序数据。</span></p>
<p><span class="yiyi-st" id="yiyi-89">最后,Fortran或C所依赖的哪个更重要,不重新排序数据或保留索引约定。</span><span class="yiyi-st" id="yiyi-90">对于大图像,重新排序数据可能是昂贵的,并且通常将索引约定反转以避免这种情况。</span></p>
<p><span class="yiyi-st" id="yiyi-91">numpy的情况使这个问题更复杂。</span><span class="yiyi-st" id="yiyi-92">numpy数组的内部机制足够灵活,可以接受任何索引排序。</span><span class="yiyi-st" id="yiyi-93">可以简单地通过操作数组的内部步长信息来重新排序索引,而不需要重新排序数据。</span><span class="yiyi-st" id="yiyi-94">Numpy将知道如何将新的索引顺序映射到数据而不移动数据。</span></p>
<p><span class="yiyi-st" id="yiyi-95">所以如果这是真的,为什么不选择匹配你最期望的索引顺序?</span><span class="yiyi-st" id="yiyi-96">特别是,为什么不定义行有序图像使用图像约定?</span><span class="yiyi-st" id="yiyi-97">(这有时被称为Fortran约定与C约定,因此是num组中数组排序的'C'和'FORTRAN'顺序选项。)</span><span class="yiyi-st" id="yiyi-98">这样做的缺点是潜在的性能损失。</span><span class="yiyi-st" id="yiyi-99">通常在数组操作中隐式地访问数据,或者通过循环遍历图像的行来显式地访问数据。</span><span class="yiyi-st" id="yiyi-100">当完成时,将以非最佳顺序访问数据。</span><span class="yiyi-st" id="yiyi-101">随着第一索引递增,实际发生的是在存储器中间隔开的元件被顺序地访问,通常存储器访问速度较差。</span><span class="yiyi-st" id="yiyi-102">例如,对于定义为使得im [0,10]表示x = 0,y = 10处的值的二维图像'im'。</span><span class="yiyi-st" id="yiyi-103">为了与通常的Python行为一致,那么im [0]将表示x = 0处的列。</span><span class="yiyi-st" id="yiyi-104">然而,该数据将被分布在整个数组上,因为数据以行顺序存储。</span><span class="yiyi-st" id="yiyi-105">尽管numpy的索引的灵活性,它不能真正地考虑事实基本操作被渲染为低效,因为数据顺序或获取连续子阵列仍然尴尬(例如,im [:,0]为第一行,vs im [ 0]),因此不能使用成语,如im中的row; for col in im does work,but does not yield continuous column data。</span></p>
<p><span class="yiyi-st" id="yiyi-106">事实证明,numpy是聪明的,当处理ufuncs确定哪个索引是最快速变化的一个在内存中,并将其用于最内层循环。</span><span class="yiyi-st" id="yiyi-107">因此,对于ufuncs,在大多数情况下,任何一种方法都没有大的内在优势。</span><span class="yiyi-st" id="yiyi-108">另一方面,使用带有FORTRAN有序数组的.flat将导致非最优的存储器访问,因为扁平数组(迭代器,实际上)中的相邻元素在存储器中不连续。</span></p>
<p><span class="yiyi-st" id="yiyi-109">事实上,列表和其他序列上的Python索引自然导致一个从外到内的顺序(第一个索引获得最大的分组,下一个最大,最后一个获得最小的元素)。</span><span class="yiyi-st" id="yiyi-110">由于图像数据通常按行存储,这对应于作为索引的最后一个项目的行内的位置。</span></p>
<p><span class="yiyi-st" id="yiyi-111">如果你想使用Fortran顺序意识到有两种方法要考虑:1)接受第一个索引只是不是在内存中最快速变化,并且所有的I / O例程在从内存到磁盘时重新排序数据或者反之亦然,或者使用numpy机制将第一索引映射到最快变化的数据。</span><span class="yiyi-st" id="yiyi-112">如果可能,我们建议前者。</span><span class="yiyi-st" id="yiyi-113">后者的缺点是,许多numpy的函数将产生数组没有Fortran排序,除非你仔细使用'order'关键字。</span><span class="yiyi-st" id="yiyi-114">这样做会非常不方便。</span></p>
<p><span class="yiyi-st" id="yiyi-115">否则,我们建议在访问数组的元素时,简单地学习反转索引的通常顺序。</span><span class="yiyi-st" id="yiyi-116">授予,它反对谷物,但它更符合Python语义和数据的自然顺序。</span></p>
</div>