快速找到非零区间

Quickly Find Non-Zero Intervals

我正在编写一种算法来确定密度图上 "mountains" 的间隔。如果有人感兴趣,该图是从 Kinect 的深处获取的。这是此算法发现的内容的快速可视化示例:(删除了小山):

我目前的算法:

def find_peak_intervals(data):
    previous = 0
    peak = False
    ranges = []
    begin_range = 0
    end_range = 0

    for current in xrange(len(data)):
        if (not peak) and ((data[current] - data[previous]) > 0):
            peak = True
            begin_range = current

        if peak and (data[current] == 0):
            peak = False
            end_range = current
            ranges.append((begin_range, end_range))

        previous = current

    return np.array(ranges)

该功能有效,但在我的笔记本电脑上需要将近 3 毫秒,我需要能够 运行 我的整个程序至少每秒 30 帧。这个函数相当丑陋,我的程序每帧必须 运行 它 3 次,所以我想要任何关于如何简化和优化这个函数的提示(可能来自 numpy 或 scipy我错过了)。

假设 pandas 数据帧如下:

    Value
0       0
1       3
2       2
3       2
4       1
5       2
6       3
7       0
8       1
9       3
10      0
11      0
12      0
13      1
14      0
15      3
16      2
17      3
18      1
19      0

您可以使用 df["Value"].shift(x) 获得连续的非零范围,其中 x 可以是 1-1 这样您就可以检查它是否以零为界.一旦你得到边界,你可以只存储它们的索引对,并在以后过滤数据时使用它们。

以下代码基于the excellent answer here by @behzad.nouri.

import pandas as pd

df = pd.read_csv("data.csv")
# Or you can use df = pd.DataFrame.from_dict({'Value': {0: 0, 1: 3, 2: 2, 3: 2, 4: 1, 5: 2, 6: 3, 7: 0, 8: 1, 9: 3, 10: 0, 11: 0, 12: 0, 13: 1, 14: 0, 15: 3, 16: 2, 17: 3, 18: 1, 19: 0}})
# --
# from 
# credits to @behzad.nouri
df['tag'] = df['Value'] > 0
fst = df.index[df['tag'] & ~ df['tag'].shift(1).fillna(False)]
lst = df.index[df['tag'] & ~ df['tag'].shift(-1).fillna(False)]
pr = [(i, j) for i, j in zip(fst, lst)]
# --

for i, j in pr:
    print df.loc[i:j, "Value"]

这给出了结果:

1    3
2    2
3    2
4    1
5    2
6    3
Name: Value, dtype: int64
8    1
9    3
Name: Value, dtype: int64
13    1
Name: Value, dtype: int64
15    3
16    2
17    3
18    1
Name: Value, dtype: int64

在 IPython 中对其计时给出以下结果:

%timeit find_peak_intervals(df)
1000 loops, best of 3: 1.49 ms per loop

这与您的速度尝试相差不远。另一种方法是使用将 pandas 系列转换为 numpy 数组并从那里进行操作。让我们使用@Warren Weckesser 的 another excellent answer,并根据您的需要对其进行修改。我们也来计时吧。

In [22]: np_arr = np.array(df["Value"])

In [23]: def greater_than_zero(a):
    ...:     isntzero = np.concatenate(([0], np.greater(a, 0).view(np.int8), [0]))
    ...:     absdiff = np.abs(np.diff(isntzero))
    ...:     ranges = np.where(absdiff == 1)[0].reshape(-1, 2)
    ...:     return ranges

In [24]: %timeit greater_than_zero(np_arr)
100000 loops, best of 3: 17.1 µs per loop

在 17.1 微秒时还不错,它也给出了相同的范围。

[1 7] # Basically same as indices 1-6 in pandas.
[ 8 10] # 8, 9
[13 14] # 13, 13
[15 19] # 15, 18