我需要一些帮助来理解查找 ndarray 中元素索引所涉及的方程。我一直在阅读 Travis A Oliphant 的《NumPy 指南》一书。 书籍链接 - Numpy 指南
在本书的第 28 页上,Travis 给出了一些方程,如果我理解正确的话,这些方程是一个计算多维数组中元素(存储在存储介质上的数组)的索引的公式风格和 Fortran 风格。我无法理解这个公式。他举的例子也没有多大意义。我正在下面粘贴该公式的图像,但如果要求不是太多,您甚至可以转到他的书的链接并滚动到第 28 页。
任何有助于理解它的帮助将不胜感激。 在此输入图片描述
我尝试在网上研究可用的资源,也尝试理解方程中使用的数学符号背后的含义,但描述不是很清楚。此外,在任何地方都没有太多关于这些方程的帮助。
让我们看看是否可以用 numpy 代码来说明这个例子。我应该补充一点,在 numpy 中,默认顺序是
C
,通常 numpy 用户不需要知道所有细节。
制作一个包含 120 个元素的一维数组 - 连续。
In [192]: x = np.arange(4*5*6)
In [193]: x
Out[193]:
array([ 0, 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,
78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
117, 118, 119])
并制作 2 个视图,一个具有默认
C
顺序,另一个 F
:
In [194]: xc = x.reshape(4,5,6); xf = x.reshape(4,5,6,order='F')
现在我将显示完整的 3D 布局,但稍后可能会压缩它们。注意顺序数字 0,1,2,... 是如何按行和列排列的:
In [195]: xc
Out[195]:
array([[[ 0, 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],
[ 78, 79, 80, 81, 82, 83],
[ 84, 85, 86, 87, 88, 89]],
[[ 90, 91, 92, 93, 94, 95],
[ 96, 97, 98, 99, 100, 101],
[102, 103, 104, 105, 106, 107],
[108, 109, 110, 111, 112, 113],
[114, 115, 116, 117, 118, 119]]])
In [196]: xf
Out[196]:
array([[[ 0, 20, 40, 60, 80, 100],
[ 4, 24, 44, 64, 84, 104],
[ 8, 28, 48, 68, 88, 108],
[ 12, 32, 52, 72, 92, 112],
[ 16, 36, 56, 76, 96, 116]],
[[ 1, 21, 41, 61, 81, 101],
[ 5, 25, 45, 65, 85, 105],
[ 9, 29, 49, 69, 89, 109],
[ 13, 33, 53, 73, 93, 113],
[ 17, 37, 57, 77, 97, 117]],
[[ 2, 22, 42, 62, 82, 102],
[ 6, 26, 46, 66, 86, 106],
[ 10, 30, 50, 70, 90, 110],
[ 14, 34, 54, 74, 94, 114],
[ 18, 38, 58, 78, 98, 118]],
[[ 3, 23, 43, 63, 83, 103],
[ 7, 27, 47, 67, 87, 107],
[ 11, 31, 51, 71, 91, 111],
[ 15, 35, 55, 75, 95, 115],
[ 19, 39, 59, 79, 99, 119]]])
进行 (1,3,2) 索引:
In [197]: xc[1,3,2]
Out[197]: 50
In [198]: xf[1,3,2]
Out[198]: 53
有一个函数可以执行从 3d 索引到 1d 索引的相同转换。 (和匹配
unravel...
):
In [199]: np.ravel_multi_index((1,3,2),(4,5,6))
Out[199]: 50
In [200]: np.ravel_multi_index((1,3,2),(4,5,6),order='F')
Out[200]: 53
除了形状之外,每个数组还有一个
strides
属性。这就是实际执行命令的内容。这些数组是“int4”,每个元素 4 个字节。因此除以 4 即可得到书中的 (30,5,1):
In [201]: xc.strides
Out[201]: (120, 24, 4)
In [204]: (1*120 + 3*24 + 2*4)/4
Out[204]: 50.0
对于“F”情况类似:
In [205]: xf.strides
Out[205]: (4, 16, 80)
In [206]: (1*4 + 3*16 + 2*80)/4
Out[206]: 53.0
因此,当我们使用像
xc/f[1,3,2]
这样的索引时,您的链接页面只是以数学形式描述了在幕后进行的计算。
对于 3d(及更高)数组,“C”和“F”并不是唯一的布局选项。我们可以将转置应用于
xc
:
In [208]: xx = xc.transpose(0,2,1); xx.strides
Out[208]: (120, 4, 24)
In [209]: xx[1,3,2]
Out[209]: 45
In [210]: xx[1]
Out[210]:
array([[30, 36, 42, 48, 54],
[31, 37, 43, 49, 55],
[32, 38, 44, 50, 56],
[33, 39, 45, 51, 57], # row 3
[34, 40, 46, 52, 58],
[35, 41, 47, 53, 59]])
通常使用“纯”
numpy
计算,我们不必担心顺序和步幅。形状/步幅参数负责处理所有这些混乱的细节。但有时数组会传递给其他已编译的代码,这些代码可能需要某些布局。如果该代码是用“c/c++”编写的,则可能需要“C”顺序数组。如果在 Fortran 中这样做,则可能会期望另一个。