Skip to content

Commit

Permalink
add tree cooradinator
Browse files Browse the repository at this point in the history
  • Loading branch information
llinjupt committed May 13, 2019
1 parent fdc2b72 commit 05d5e73
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 6 deletions.
1 change: 1 addition & 0 deletions appendix.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Python相关
- `Python 从入门到深入 <https://pythonhowto.readthedocs.io/zh_CN/latest/>`_
- `Module of the Week <https://pymotw.com/3/>`_
- `Numpy 和 Scipy 官网 <https://docs.scipy.org/doc/>`_
- `NumPy 中文文档 <https://numpy.org.cn/index.html>`_
- `在线 octave 开发环境 <https://octave-online.net/>`_

数据相关
Expand Down
Binary file added imgs/numpy/treeindex.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 66 additions & 6 deletions numpy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ octave 每新增一个轴,原来的轴保持不变,新增的轴变为新的

Numpy 3D 数组坐标示例

理解了数组坐标轴的区别,那么就真正理解索引机制了。我们会发现使用直角坐标系表示 2D 数组时可以把第一维称为行(row),第二维称为列(column),第三维并没有固定的称谓,可以认为是深度(depth),高度(height)或者层(slice )。但是一旦超过 3 维,这种表示方法就将无能为力了,实际上索引只是一个树形结构,后面将会展示这种更具弹性的表示方法。
理解了数组坐标轴的区别,那么就真正理解索引机制了。我们会发现使用直角坐标系表示 2D 数组时可以把第一维称为行(row),第二维称为列(column),第三维并没有固定的称谓,可以认为是深度(depth),高度(height)或者片/层(slice/layer)。但是一旦超过 3 维,这种表示方法就将无能为力了,实际上索引只是一个树形结构,后面将会展示这种更具弹性的表示方法。

另一个问题是 order 参数的作用,在 2D 数组中,如果指定了 order = 'F',那么 Numpy 和 Octave 的打印结果就是一致的,我们也已经指出它们的行列规定是一致的。
然而如果生成的是 3D 数组,那么 np.array 参数即便指定了 order = 'F' 参数,生成的 3D 数组打印出来依然是不同的,这是因为坐标系规定不同。
Expand Down Expand Up @@ -577,19 +577,79 @@ Numpy 同时支持两种内存布局,可以通过 order 参数指定。在大
示例中数据元素达到了 1 亿个,此时性能相差 30 倍,所以在进行大数据处理时一定要正确设置内存布局。
树形坐标
~~~~~~~~~~~~
无论是笛卡尔坐标系,还是其他坐标系,本身就是一个索引系统。通过坐标系只能很好地描述 1D-3D 数组的索引,超过 3D 就无能为力了。
从自然的角度使用维度有限的 3D 空间来模拟可以有无限维的索引本身就是不合理的,那么就让索引回归索引,思考邮政系统是如何投递信笺的:地址就是索引最自然的应用。
一个地址可以由:国家,省份,城市,街道,小区,楼号,层号,房号构成,显然这就是一个树形图,采用树形坐标可以完美地描述任意维数组。
.. figure:: imgs/numpy/treeindex.png
:scale: 100%
:align: center
:alt: treeindex
树形坐标
上图描述了一个 shape(2,2,2) 的 3D 数组,为了作图简便,最后两维保留了行列的2维形状,这样树形图就少了一次分支,不会显得太臃肿。
尽管我们极力想把最后两维想象成行或列,正如前所述,我们可以把任意两维作为行或列。我们可能认为一幅彩色图片在 Numpy 应该以 [RGB,W,H] 的索引方式来处理,这样行就对应了高度,列就对应了宽度,第一维描述各个通道。实际上并非如此,而是以 [W,H,RGB] 的方式处理的,这样 Numpy 的树形坐标就和 Octave/Matlab 保持一致了。
.. code-block:: python
:linenos:
:lineno-start: 0
from matplotlib import pyplot as plt
img = plt.imread("lena.png")
print(type(img).__name__)
print(img.shape)
>>>
ndarray
(256, 256, 3)
实际应用中应该根据具体数据,来变换索引(坐标)以最形象直观的方式描述,而不要拘泥于总是把最后两维看做行和列。
数组视图
~~~~~~~~~~~~
在理解数组视图之前,理解不同的内存布局(Memory Layouts)是有益的,
NumPy 中提供了大量的对数组进行处理的函数,这些函数返回的新数组中的元素和原数组元素具有两种关系:
- 引用,也即不对原数组中元素复制,修改元素会相互影响。
- 复制,拷贝副本,修改不会互相影响。包含简单索引(例如简单索引和切片组合使用)的引用方式,均会进行复制。
一个数组被称为数组包含的数据的一个视图(view),所以如果是引用返回的数组,则称为数据的另一个视图。不同视图是对数据的不同观察方式,体现在数组上就是形式的变形,不会拷贝任何东西。视图也被称为视窗。例如同样是 4 个元素,可以是 2x22 维数组,也可以组成 1x4 的向量或者 4x12 维数组,它们均是同一组数据的不同视图。
如何查看一个对象是视图,还是拥有 data 的原数组呢? ndarray.base 记录引用的原数组,所以如果 ndarray.base 不是 None,那么它就是视图,且原数组对象就是 ndarray.base。
.. code-block:: python
:linenos:
:lineno-start: 0
In [280]: a = np.array([1,2])
In [281]: a.base
In [282]: b = a.reshape(2,1)
In [283]: b.base
Out[283]: array([1, 2])
In [284]: b.base is a
Out[284]: True
不要使用 id(a) == id(b) 判断是否为视图,它们可能相等,也不要使用 id(a.data) == id(b.data) 判断视图,因为 data 是 memoryview 对象,不同的 memoryview 对象可能引用同一块内存区域,但是 memoryview 自身的地址是不同的。
.. code-block:: python
:linenos:
:lineno-start: 0
In [289]: a = memoryview(b'123')
In [290]: a # 此地址是 memoryview 自身的地址,不是它引用的对象地址
Out[290]: <memory at 0x0000029C61D18708>
步长 strides 是另一个 ndarray 对象成员,它对于理解数组视图至关重要。
.. code-block:: python
Expand All @@ -600,10 +660,10 @@ NumPy 中提供了大量的对数组进行处理的函数,这些函数返回
[4, 5, 6],
[7, 8, 9]], dtype=np.int8)
t = x.T
print(id(x.data), id(t.data))
print(t.base is x)
>>>
1621569473776 1621569473776
True
转置不会复制数据,所以 t 和 x 的 data 地址是相同的。但是它们的 stides 是不同的:
Expand Down Expand Up @@ -1750,7 +1810,7 @@ logspace() 等价于先等差再对元素以底数 base 乘幂:
.. admonition:: 注意
ndarray 切片操作不会复制数据,新数组是原数组的一个视图,这和 Python 切片浅拷贝有本质区别,简单索引会复制。可以使用 id() 函数查看对象是否为视图
ndarray 切片操作不会复制数据,新数组是原数组的一个视图,这和 Python 切片浅拷贝有本质区别,简单索引会复制。可以使用 a.base is not None 查看对象是否为视图
一维数组切片
``````````````
Expand Down

0 comments on commit 05d5e73

Please sign in to comment.