【问题标题】:Move index to the left, according to grid根据网格将索引向左移动
【发布时间】:2018-07-24 13:14:02
【问题描述】:

假设我有一个包含允许和禁止区域的网格。

import numpy as np
forbidden = np.array([False, False, False, False, False, False,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True, False, False, False, False, False, False,
       False, False,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True, False, False, False,
       False, False, False, False, False, False, False, False, False,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  False])

现在,我在 (0, 149) 之间有一堆网格点。如果他们似乎在禁区,我想“将他们移到左边”直到他们离开禁区。

例如,

idx = 13
forbidden[idx]  # is True, hence we want to move to the left
forbidden[5] # is False, and it is the "first index" where this is such

同时,

idx = 5
forbidden[idx] = # is False, so this index stays the way it is

因此,getCorrectIndex(13) == getCorrectIndex(5) == 5

一种低效的编码方式:

def getCorrectIndex(idx, forbidden):
    # this trusts that the forbidden[0] == False, which is Okay
    for remove in range(0, idx):
        if not forbidden[idx - remove]:
            return idx - remove

对索引数组执行此操作的矢量化方法是最佳的,但如果没有那个愚蠢的循环,我什至无法想出正确的方法...我会很感激这里的任何指针!

【问题讨论】:

  • 如果 0 和 4 之间的第一个值是 True,会发生什么? idx3?
  • @HaleemurAli 你可以相信第一个元素是False

标签: python numpy vectorization


【解决方案1】:

这是一种方法。不过,不确定您是否会比您的解决方案更喜欢它。

第一步,获取所有False节点的索引:

b = np.where(forbidden == False)[0]

这将返回

array([  0,   1,   2,   3,   4,   5,  30,  31,  32,  33,  34,  35,  36,
    37,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,
    90,  91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102,
   103, 104, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
   131, 132, 133, 134, 135, 136], dtype=int64)

之后,在该列表的切片部分再次使用whereamax,以获得低于当前索引的最高索引。然后使用b 上的索引来获得forbidden 数组上的最高False 索引。

>>> index = 15
>>> first_left_false = b[np.amax(np.where(b<index))]
>>> first_left_false
5

函数可能如下所示:

def getCorrectIndex(index, forbidden):
    if forbidden[index] == False:
         return index
    b = np.where(forbidden == False)[0]
    res = b[np.amax(np.where(b<index))]
    return res

或者,如果您想预先计算 b 数组,您可以稍微缩短它:

def getCorrectIndex(index, b, forbidden):
     if forbidden[index] == False:
         return index
     else:
         return b[np.amax(np.where(b<index))]

【讨论】:

    【解决方案2】:

    您可以使用np.maximum.accumulate 将布尔数组转换为最近允许的索引,然后只需查找idx-th 元素

    首先创建一个索引数组,将禁止位置设置为0。accumulate返回函数的滚动应用,在这种情况下maximummaximummax的不同之处在于它应用于两个数组,返回按元素的最大数组。

    lookup = np.arange(len(forbidden))
    lookup[forbidden] = 0
    lookup = np.maximum.accumulate(lookup)
    
    lookup[13]
    # 5
    lookup[5]
    # 5
    lookup[149]
    # 136
    

    改为右移

    将禁止位置设置为大于或等于数组长度的任何值,并在反向查找数组上使用np.minimum.accumulate。这与上面的左移方法基本相同。

    lookup[forbidden] = len(forbidden)
    lookup = np.minimum.accumulate(lookup[::-1])[::-1]
    
    lookup[136]
    # 136
    lookup[137]
    # 150
    

    【讨论】:

    • 请注意forbidden[136] == False,所以我希望算法会为之后的数字返回136——但它会返回57
    • cumsum() 适用于第一个 True 值,但它的编写方式不会在每个 True 集合的末尾添加“跳过”索引。
    • 出于好奇:有没有直接向右移动而不是向左移动的方法?
    • 那么你将如何处理索引136
    • 更改forbidden[136] = False,您可以依赖允许的角值。我对标准算法感兴趣,而不是为极端情况添加的所有补丁。
    猜你喜欢
    • 1970-01-01
    • 2014-04-01
    • 2022-08-18
    • 1970-01-01
    • 2014-06-10
    • 2012-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多