在numpy数组中查找相同值的序列长度(运行长度编码)

时间:2009-07-01 00:19:18

标签: python matlab numpy matplotlib

在一个pylab程序中(也可能是一个matlab程序)我有一个代表距离的numpy数字数组:d[t]是时间t距离 (我的数据的时间跨度是len(d)个时间单位。)

我感兴趣的事件是当距离低于某个阈值时,我想计算这些事件的持续时间。使用b = d<threshold很容易得到一组布尔值,问题归结为计算b中纯真词的长度序列。但我不知道如何有效地做到这一点(即使用numpy原语),并且我使用了数组并进行手动更改检测(即当值从False变为True时初始化计数器,只要值为True就增加计数器,当值返回False时,将计数器输出到序列。但这非常缓慢。

如何在numpy数组中有效地检测那种序列?

下面是一些说明我的问题的python代码:第四个点需要很长时间才能出现(如果没有,增加数组的大小)

from pylab import *

threshold = 7

print '.'
d = 10*rand(10000000)

print '.'

b = d<threshold

print '.'

durations=[]
for i in xrange(len(b)):
    if b[i] and (i==0 or not b[i-1]):
        counter=1
    if  i>0 and b[i-1] and b[i]:
        counter+=1
    if (b[i-1] and not b[i]) or i==len(b)-1:
        durations.append(counter)

print '.'

4 个答案:

答案 0 :(得分:43)

虽然不是numpy原语,但itertools函数通常非常快,所以请尝试一下(并测量包括这个在内的各种解决方案的时间):

def runs_of_ones(bits):
  for bit, group in itertools.groupby(bits):
    if bit: yield sum(group)

如果确实需要列表中的值,当然可以使用list(runs_of_ones(bits));但也许列表理解可能会略微加快:

def runs_of_ones_list(bits):
  return [sum(g) for b, g in itertools.groupby(bits) if b]

转向“numpy-native”的可能性,怎么样:

def runs_of_ones_array(bits):
  # make sure all runs of ones are well-bounded
  bounded = numpy.hstack(([0], bits, [0]))
  # get 1 at run starts and -1 at run ends
  difs = numpy.diff(bounded)
  run_starts, = numpy.where(difs > 0)
  run_ends, = numpy.where(difs < 0)
  return run_ends - run_starts

再次:确保在实际的示例中针对彼此进行基准测试!

答案 1 :(得分:8)

这是一个仅使用数组的解决方案:它接受一个包含bool序列的数组,并计算转换的长度。

>>> from numpy import array, arange
>>> b = array([0,0,0,1,1,1,0,0,0,1,1,1,1,0,0], dtype=bool)
>>> sw = (b[:-1] ^ b[1:]); print sw
[False False  True False False  True False False  True False False False
  True False]
>>> isw = arange(len(sw))[sw]; print isw
[ 2  5  8 12]
>>> lens = isw[1::2] - isw[::2]; print lens
[3 4]

sw包含一个true,其中有一个开关,isw在索引中转换它们。然后在lens中成对地减去isw的项目。

请注意,如果序列以1开始,它将计算0s序列的长度:这可以在索引中修复以计算镜头。此外,我还没有测试过角情况,例如长度为1的序列。


返回所有True - 子阵列的起始位置和长度的完整函数。

import numpy as np

def count_adjacent_true(arr):
    assert len(arr.shape) == 1
    assert arr.dtype == np.bool
    if arr.size == 0:
        return np.empty(0, dtype=int), np.empty(0, dtype=int)
    sw = np.insert(arr[1:] ^ arr[:-1], [0, arr.shape[0]-1], values=True)
    swi = np.arange(sw.shape[0])[sw]
    offset = 0 if arr[0] else 1
    lengths = swi[offset+1::2] - swi[offset:-1:2]
    return swi[offset:-1:2], lengths

针对不同的bool 1D阵列进行测试(空数组;单个/多个元素;偶数/奇数长度;以True / False开头;仅True / {{1} } elements)。

答案 2 :(得分:6)

万一有人好奇(因为你传递了MATLAB),这是在MATLAB中解决它的一种方法:

threshold = 7;
d = 10*rand(1,100000);  % Sample data
b = diff([false (d < threshold) false]);
durations = find(b == -1)-find(b == 1);

我对Python不太熟悉,但也许这可以帮助你提供一些想法。 =)

答案 3 :(得分:0)

durations = []
counter   = 0

for bool in b:
    if bool:
        counter += 1
    elif counter > 0:
        durations.append(counter)
        counter = 0

if counter > 0:
    durations.append(counter)