计算Numpy数组中特定的正确/错误排序的出现次数

时间:2018-11-09 13:27:00

标签: python arrays numpy pattern-matching

我有一个Numpy的True和False值数组,例如:

test = np.array([False, False, False, True, False, True, False, True, False,False, False, False, True, True, False, True])

我想知道以下模式(False,True,False)在数组中发生的次数。在上面的测试中,它将是4。这不是唯一的模式,但是我认为当我理解这段代码时,我可能还会制作其他代码。

当然,我可以遍历数组。如果第一个值相等,则比较下一个,否则转到循环中的下一个值。像这样:

totalTimes=0
def swapToBegin(x):
    if(x>=len(test)):
        x-=len(test)
    return(x)
for i in range(len(test)):
    if(test[i]==False):
        if(test[swapToBegin(i+1)]==True):
            if test[swapToBegin(i+2)]==False:
                totalTimes += 1

但是,由于我需要多次执行此操作,因此这段代码非常慢。由于可以快速显示我的需求,因此无法进行任何改进。但是必须有更好的解决方案。

有没有更好的方法来搜索数组中的模式?它不需要合并数组的结尾和开头,因为我以后可以做到这一点。但是,如果可以包含它,那就太好了。

2 个答案:

答案 0 :(得分:0)

您可以使用包含[False,True,False]的数组,然后搜索它。

searchfor = np.array([False, True, False])

答案 1 :(得分:0)

您尚未提供test的大小的详细信息,因此对于我使用的方法的基准,它有1000个元素。下一个重要的部分是实际分析代码。您不能说它慢(或快),直到有可靠的数字来备份它。您的代码在我的计算机上运行大约1.49毫秒。

您通常可以通过删除python循环并将其替换为numpy函数来获得numpy的改进。 因此,与其单独测试每个元素(很多if条件可能会减慢速度),不如将它们全部放入一个数组比较中,然后使用all来检查每个元素是否匹配。

check = array([False, True, False])
sum([(test[i:i+3]==check).all() for i in range(len(test) - 2)])

分析显示它运行在1.91毫秒内。

实际上是倒退了一步。那么,是什么原因导致减速?好吧,使用[]进行数组访问会创建一个新的数组对象,该对象可能是其中的一部分。更好的方法可能是使用偏移创建一个大数组,然后使用广播进行比较。

sum((c_[test[:-2], test[1:-1], test[2:]] == check).all(1))

这次将check与数组c_[test[:-2], test[1:-1], test[2:]]的每一行进行比较。 all的轴参数(1)仅用于计算每个元素匹配的行。这运行在40.1us。这是一个巨大的进步。

当然,就复制元素而言,创建要广播的数组将花费大量成本。为什么不直接进行比较?

sum(all([test[i:len(test)-2+i]==v for i, v in enumerate(check)], 0))

运行时间为18.7us。

加快速度的最后一个想法是使用as_strided。这是一个高级技巧,可以更改数组的步幅以获取偏移数组而不复制任何数据。通常这不值得付出努力,但我在这里只是为了好玩而已。

sum((np.lib.index_tricks.as_strided(test, (len(test) - len(check) + 1, len(check)), test.strides + (1, )) == check).all(1))

这也大约需要40us。因此,在这种情况下,无需付出额外的努力。