【问题标题】:Quickly Find Non-Zero Intervals快速找到非零区间
【发布时间】:2015-02-28 03:32:13
【问题描述】:

我正在编写一个算法来确定密度图上“山”的间隔。如果有人感兴趣,该情节是从 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 的东西)。

【问题讨论】:

  • 看起来这个算法主要是一个零查找器。一旦找到一个非零元素,它就会退出比较连续元素并再次寻找零。
  • 所以更像是 'if peak and (data[current] == 0):' 后跟 'elif (not peak) and (data[current] != 0):'?跨度>

标签: python optimization numpy scipy signal-processing


【解决方案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

【讨论】:

  • 谢谢!这两种方法都非常有效。我的数据已经是 numpy 格式,所以你的第二种方法非常适合。时间从我系统上的 3 毫秒(我知道我有一个土豆)减少到 0.21 毫秒。
猜你喜欢
  • 1970-01-01
  • 2015-07-27
  • 2011-02-06
  • 1970-01-01
  • 2014-01-16
  • 2022-01-20
  • 1970-01-01
  • 2019-07-17
  • 1970-01-01
相关资源
最近更新 更多