快速找到非零区间
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
我正在编写一种算法来确定密度图上 "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