np.unravel_index的直观解释是什么?

83
我已经阅读了np.unravel_index的文档,并尝试使用这个函数,但是我无法弄清楚它在做什么。

1
根据形成这些轴的nd网格的形状,将线性索引转换为沿每个轴的索引。在np.ravel_multi_index中,这里有一些解释可以反过来理解。 - Divakar
6个回答

112

计算机内存是线性寻址的。每个内存单元对应一个数字。可以通过基地址和索引来寻址一块内存。例如,假设基地址为10,000:

item index      0       1       2       3
memory address  10,000  10,001  10,002  10,003

为了存储多维块,必须将它们的几何形状以某种方式放在线性内存中。在CNumPy中,这是逐行完成的。一个2D示例如下:

  | 0      1      2      3
--+------------------------
0 | 0      1      2      3
1 | 4      5      6      7
2 | 8      9     10     11

例如,在这个3x4的块中,二维索引(1, 2)对应于线性索引6,即1 x 4 + 2

unravel_index函数执行的是相反的操作。给定一个线性索引和块的维度,它会计算出对应的ND索引。因此,在我们的示例中,我们可以通过线性索引6获得原始的二维索引(1, 2)

>>> np.unravel_index(6, (3, 4))
(1, 2)
注意:上述内容略去了一些细节。1)将项索引转换为内存地址还必须考虑项大小。例如,整数通常有4或8个字节。因此,在后一种情况下,项目i的内存地址将为base + 8 x i。2)NumPy比建议的更加灵活。如果需要,它可以按列组织ND数据。它甚至可以处理在内存中不连续但留有间隔等的数据等问题。
额外阅读:ndarray的内部内存布局

1
我只是好奇想更深入地了解这个。请问在哪里可以找到更多相关信息?有什么建议吗? - kmario23
3
@kmario23 https://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html - Paul Panzer
1
@kmario23,您可以在此处访问它:https://numpy.org/doc/stable/reference/generated/numpy.unravel_index.html。这是官方文档中的内容。 - Shiva Shankar
对于一个方阵,可以计算索引:np.unravel_index(i, (n, n)) == (i//n, i%n) - Andy

83

我们将从文档中的一个示例开始。

>>> np.unravel_index([22, 41, 37], (7,6))
(array([3, 6, 6]), array([4, 5, 1]))

首先,(7,6) 指定了我们想要将索引转换回来的目标数组的维度。其次,[22, 41, 37] 是该数组在被展平后的一些索引。如果一个大小为7乘6的数组被展平,它的索引看起来会像是:

[ 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*]

如果我们将这些索引展开,回到它们在 (7, 6) 数组中的原始位置,那么它将是:

如果我们将这些索引展开,回到它们在 (7, 6) 数组中的原始位置,那么它将是:

      [[ 0,   1,   2,   3,   4,   5],
       [ 6,   7,   8,   9,  10,  11],
       [12,  13,  14,  15,  16,  17],
       [18,  19,  20,  21, *22*, 23],  <- (3, 4)
       [24,  25,  26,  27,  28,  29],
       [30,  31,  32,  33,  34,  35],
       [36, *37*, 38,  39,  40, *41*]]
           (6, 1)               (6,5)
unravel_index函数的返回值告诉您,如果数组未被展平,[22, 41, 37]应该是什么索引。 如果数组未被展平,这些索引应该是[(3, 4), (6, 5), (6,1)]。 换句话说,该函数将展平数组中的索引转换回其未展平版本。

https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.unravel_index.html


老实说,我认为在你的示例中输出应该是[(3, 4), (6, 5), (6,1)],而不是文档中的转置,以便与np.unravel_index(1621, (6,7,8,9))的输出一致,后者为(3, 1, 4, 1)。 - SYK

53

这段内容与其他两个答案的内容没有区别,但它可能更易于理解。如果您有一个二维矩阵或数组,您可以用不同的方式引用它。您可以输入(row,col)以获取(row,col)处的值,或者为每个单元格分配单个数字索引。unravel_index只是在这两种引用矩阵中的值的方式之间进行转换。

输入图像描述

这可扩展到大于2的维度。您还应该知道np.ravel_multi_index(),它执行反向转换。请注意,它需要数组的(行,列)和形状。

我还看到我在索引矩阵中有两个10-糟糕。


4
这实际上正是我在直觉方面寻找的,谢谢。我可以问一下,做这件事的动机是否仅仅是因为它使计算变得不那么复杂/更容易存储在记忆中? - austinkjensen
3
我想可能有很多原因和应用场景。我个人经常使用的一种方式是这样的:我有一个由单像素宽度构成的骨架,需要沿着它行走并返回所走过的坐标。使用“索引”空间比“行、列”空间更简单,因为它可以把操作次数减半。举个例子,如果你想知道自己是否已经走到了(2,1),你需要检查2,然后再检查1。而若使用索引,我只需要检查“7”。这只是一个基本的例子,但确实会简化很多事情。请注意,还有许多其他的应用场景 :) - Jon

4
我可以用非常简单的例子来解释。这适用于np.ravel_multi_index以及np.unravel_index。
>>> X = np.array([[4,  2],
                  [9,  3],
                  [8,  5],
                  [3,  3],
                  [5,  6]])
>>> X.shape
(5, 2)

在X中查找所有值为3的位置:
>>> idx = np.where(X==3)
>>> idx
(array([1, 3, 3], dtype=int64), array([1, 0, 1], dtype=int64))

我将翻译如下内容:

例如:x = [1,3,3]y = [1,0,1],它会返回索引的 x 和 y 值(因为 X 是二维的)。


如果您对获得的 idx 应用ravel_multi_index

>>> idx_flat = np.ravel_multi_index(idx, X.shape)
>>> idx_flat
array([3, 6, 7], dtype=int64)

idx_flat是X中数值为3的元素的线性索引。

从上面的示例中,我们可以理解:

  • ravel_multi_index将多维索引(nd数组)转换为单维索引(线性数组)
  • 它仅适用于索引,即输入和输出都是索引

结果索引将是X.ravel()的直接索引。您可以在下面的x_linear中进行验证:

>>> x_linear = X.ravel()
>>> x_linear
array([4, 2, 9, 3, 8, 5, 3, 3, 5, 6])

相比之下,unravel_index 非常简单,只是上面函数(np.ravel_multi_index)的反转。

>>> idx = np.unravel_index(idx_flat , X.shape)
>>> idx
(array([1, 3, 3], dtype=int64), array([1, 0, 1], dtype=int64))

这与idx = np.where(X==3)相同。

  • unravel_index将单维度索引(线性数组)转换为多维度索引(nd数组)
  • 它仅适用于索引,即输入和输出都是索引

0

给定一个指向.ravel()ed数组的raveled_indexnp.unravel_index会计算出等效于基础数组的展平索引:

import numpy as np

my_array = np.random.random((100, 42))
raveled_array = my_array.ravel()

raveled_index = 1337
unraveled_index = np.unravel_index(raveled_index, my_array.shape)

assert raveled_array[raveled_index] == my_array[unraveled_index]


两个需要了解的知识点:

  1. raveled_array也被称为flat_array;因此,raveled_indexflat_index仅是指向“压平”数组的索引。此外,由于压平数组已经“丢失”了其原始形状信息,因此在调用np.unravel_index时需要添加这些信息。

  2. unraveled_index通常被称为multi_index。这是因为你需要多个值(一个N元组)来索引任何具有my_array.dim == N的数组中的元素。因此,np.unravel_index的反函数被称为np.ravel_multi_index


-1

这仅适用于2D情况,但在此情况下,np.unravel_index函数返回的两个坐标相当于执行floor除法并分别应用模函数。

for j in range(1,1000):
    for i in range(j):
        assert(np.unravel_index(i,(987654321,j))==(i//j,i%j))

形状数组的第一个元素(即987654321)除了限制可以通过函数传递的展开线性索引的最大值之外,没有其他意义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接