【问题标题】:How to get a reverse mapping in numpy in O(1)?如何在 O(1) 中的 numpy 中获得反向映射?
【发布时间】:2019-06-06 18:34:45
【问题描述】:

我有一个numpy数组,它的元素是唯一的,例如:

b = np.array([5, 4, 6, 8, 1, 2])

(Edit2:b 可以有大数和浮点数。上面的例子是为了简单起见)

我得到数字,它们是 b 中的元素。

我想在b 中找到它们的索引,这意味着我想在b 中进行从值到索引的反向映射

我可以的

for number in input:
    ind = np.where(number==b)

每次调用 where 时都会遍历整个数组。

我也可以创建一个字典,

d = {}
for i, element in enumerate(list(b)):
    d[element] = i

我可以在“预处理”时创建这个字典,但我仍然会留下一个看起来很奇怪的字典,大部分是 numpy 代码,这似乎(在我看来)不是 numpy 的用途。

如何在 numpy 中进行这种反向映射?

使用情况(需要 O(1) 时间和内存):

print("index of 8 is: ", foo(b, 8))

  • Edit1:不是this 的副本

按照here 的解释使用 in1d 并不能解决我的问题。使用他们的例子:

b = np.array([1, 2, 3, 10, 4])

我希望能够在运行时在 O(1) 中找到例如 10 在 b 中的索引。

进行预处理

mapping = np.in1d(b, b).nonzero()[0]

>> [0, 1, 2, 3, 4]

(可以使用np.arange(len(b)) 完成)

并没有真正的帮助,因为当10 作为输入时,用这种方法不可能在 O(1) 时间内告诉它的索引。

【问题讨论】:

  • 是一维数组吗?重复项呢?等等等等
  • 也许是code golf 的好人选?
  • @uhoh 我支持高尔夫的建议,但首先 OP 需要确定他实际上想要在其中存储查找表的数据结构
  • @uhoh 你最后打了个高尔夫球吗?如果有,可以给个链接吗?
  • @Gulzar 到目前为止我已经制作了one golf,但这与此无关。去吧! :-)

标签: python arrays numpy indexing


【解决方案1】:

通过利用 numpy 的高级索引,它比您想象的要简单。

我们要做的是创建我们的目标数组,然后将 ussign b 分配为索引。我们将使用 arange 分配我们想要的索引。

>>> t = np.zeros((np.max(b) + 1,))
>>> t[b] = np.arange(0, b.size)
>>> t
array([0., 4., 5., 0., 1., 0., 2., 0., 3.])

您可以使用nans 或 -1 而不是零来构造目标以帮助检测无效查找。

内存使用:这在空间和时间上都是最佳性能,因为它完全由 numpy 处理。

如果你能容忍冲突,你就可以实现一个穷人的哈希表。假设我们有货币,例如:

h = np.int32(b * 100.0) % 101  # Typically some prime number
t = np.zeros((101,))
t[h] = np.arange(0, h.size)

# Retrieving a value v; keep in mind v can be an ndarray itself.
t[np.int32(v * 100.0) % 101]

如果您知道您的数据集是什么样子,您可以执行任何其他步骤来修改地址。

这是关于 numpy 有用的限制。

【讨论】:

  • 如果 b 有一个值为99999.4的元素怎么办?
  • @Gulzar 查看我对我的回答的评论。我相信用 numpy 实现这一点根本不切实际。
  • 我正在回答这个问题,它表示整数和紧凑数组。如果您需要通用映射,请使用字典,但这不是问题所在。如果您尝试查找浮点数,则需要对其进行规范化以处理舍入错误。
【解决方案2】:

解决方案

如果您想要恒定时间(即O(1)),那么您需要预先计算某种查找表。如果您想使用另一个 Numpy 数组创建查找表,它实际上必须是一个稀疏数组,其中大多数值是“空的”。这是一种可行的方法,其中将空值标记为-1

b = np.array([5, 4, 6, 8, 1, 2])

_b_ix = np.array([-1]*(b.max() + 1))
_b_ix[b] = np.arange(b.size)
# _b_ix: array([-1,  4,  5, -1,  1,  0,  2, -1,  3])

def foo(*val):
    return _b_ix[list(val)]

测试:

print("index of 8 is: %s" % foo(8))
print("index of 0,5,1,8 is: %s" % foo(0,5,1,8))

输出:

index of 8 is: [3]
index of 0,5,1,8 is: [-1  0  4  3]

警告

正如其他回答者指出的那样,在生产代码中,您绝对应该使用字典来解决这个问题。为什么?好吧,一方面,假设您的数组b 包含float 值,或任何非int 值。那么基于 Numpy 的查找表将根本不起作用。

因此,只有当您对使用字典有根深蒂固的哲学反对意见时(例如,dict 碾过您的宠物猫),您才应该使用上述答案。 这是生成反向查找字典的好方法:

ix = {k:v for v,k in enumerate(b.flat)}

【讨论】:

  • 在 b 包含整数的情况下,完全 numpy 解决方案和字典解决方案之间的 cpu 时间是否存在差异?
【解决方案3】:

您可以使用dictzipnumpy.arrange 来创建反向查找:

import numpy 

b = np.array([5, 4, 6, 8, 1, 2])
d = dict(zip(b, np.arange(0,len(b))))
print(d)

给予:

{5: 0, 4: 1, 6: 2, 8: 3, 1: 4, 2: 5}

【讨论】:

    【解决方案4】:

    如果您想进行多次查找,可以在初始 O(n) 遍历之后在 O(1) 中执行这些操作以创建查找字典。

    b = np.array([5, 4, 6, 8, 1, 2])
    lookup_dict = {e:i for i,e in enumerate(b)}
    def foo(element):
        return lookup_dict[element]
    

    这适用于您的测试:

    >>> print('index of 8 is:', foo(8))
    index of 8 is:  3
    

    请注意,如果自上次调用 foo() 以来 b 可能已更改,我们必须重新创建字典。

    【讨论】:

    • 数组永远不会改变。至于答案,你不是完全按照我在我的问题中写的吗?
    • @Gulzar 我的错,我没有完全阅读这个问题!进一步考虑,我认为这是创建反向映射的最佳方式。 numpy 无法实现这一点,因为该模块不支持“类似查找”的结构;只有数组。因此,在 numpy 中实现这一点的唯一方法是创建一个海量数组,其中每个元素的索引是 b 中每个元素的值,每个元素的值是 @987654328 中该值的索引的索引@。这将使用大量内存(因为元素将是 0)并且也不会考虑负数或浮点数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-28
    • 1970-01-01
    • 2014-04-03
    • 1970-01-01
    相关资源
    最近更新 更多