NumPy ndarray 非连续内存布局

问题描述 投票:0回答:1

我需要一些帮助来理解查找 ndarray 中元素索引所涉及的方程。我一直在阅读 Travis A Oliphant 的《NumPy 指南》一书。 书籍链接 - Numpy 指南

在本书的第 28 页上,Travis 给出了一些方程,如果我理解正确的话,这些方程是一个计算多维数组中元素(存储在存储介质上的数组)的索引的公式风格和 Fortran 风格。我无法理解这个公式。他举的例子也没有多大意义。我正在下面粘贴该公式的图像,但如果要求不是太多,您甚至可以转到他的书的链接并滚动到第 28 页。

任何有助于理解它的帮助将不胜感激。 在此输入图片描述

我尝试在网上研究可用的资源,也尝试理解方程中使用的数学符号背后的含义,但描述不是很清楚。此外,在任何地方都没有太多关于这些方程的帮助。

numpy multidimensional-array scipy numpy-ndarray equation
1个回答
0
投票

让我们看看是否可以用 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 中这样做,则可能会期望另一个。

© www.soinside.com 2019 - 2024. All rights reserved.