快速查找非零间隔

时间:2015-02-28 03:32:13

标签: python optimization numpy scipy signal-processing

我正在编写一个算法来确定" mountains"在密度图上。如果有人感兴趣的话,情节取自Kinect的深度。以下是该算法发现的快速可视化示例:(删除了小山):

Example image

我目前的算法:

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)。

1 个答案:

答案 0 :(得分:1)

假设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 https://stackoverflow.com/questions/24281936
# 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数组并从那里进行操作。让我们来看看another excellent answer,@ Warren Weckesser,并根据您的需要进行修改。我们也是时候了。

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