在时间序列数据,尤其是传感器类数据中连续下降或连续增加,对判断传感器监测是真异常还是假异常,有重要的参考作用。如何实现计算呢?
>>> import pandas as pd
>>> times = ['11:55', '12:00', '12:05', '12:10', '12:15', '12:20', '12:25', '12:30', '12:35', '12:40', '12:45', '12:50', '12:55', '13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30']
>>> values = [0.940353, 0.919144, 0.909454, 0.904968, 0.867957, 0.901426, 0.94733, 0.770106, 0.741985, 0.671444, 0.558297, 0.496972, 0.457803, 0.446388, 0.430217, 0.379902, 0.321828, 0.298304, 0.442079, 0.634764, ]
>>> df = pd.DataFrame(data={'time': times, 'value': values})
>>> df['is_decline'] = df.value.diff() < 0
>>> df['is_decline_num'] = df['is_decline'] * 1
>>> y = df['is_decline_num']
>>> df['decline_continuity'] = y * (y.groupby((y != y.shift()).cumsum()).cumcount() + 1)
no | time | value | is_decline | is_decline_num | decline_continuity |
---|---|---|---|---|---|
0 | 11:55 | 0.940353 | False | 0 | 0 |
1 | 12:00 | 0.919144 | True | 1 | 1 |
2 | 12:05 | 0.909454 | True | 1 | 2 |
3 | 12:10 | 0.904968 | True | 1 | 3 |
4 | 12:15 | 0.867957 | True | 1 | 4 |
5 | 12:20 | 0.901426 | False | 0 | 0 |
6 | 12:25 | 0.94733 | False | 0 | 0 |
7 | 12:30 | 0.770106 | True | 1 | 1 |
8 | 12:35 | 0.741985 | True | 1 | 2 |
9 | 12:40 | 0.671444 | True | 1 | 3 |
10 | 12:45 | 0.558297 | True | 1 | 4 |
11 | 12:50 | 0.496972 | True | 1 | 5 |
12 | 12:55 | 0.457803 | True | 1 | 6 |
13 | 13:00 | 0.446388 | True | 1 | 7 |
14 | 13:05 | 0.430217 | True | 1 | 8 |
15 | 13:10 | 0.379902 | True | 1 | 9 |
16 | 13:15 | 0.321828 | True | 1 | 10 |
17 | 13:20 | 0.298304 | True | 1 | 11 |
18 | 13:25 | 0.442079 | False | 0 | 0 |
19 | 13:30 | 0.634764 | False | 0 | 0 |
上述代码中最核心的就属y * (y.groupby((y != y.shift()).cumsum()).cumcount() + 1):
Step 1: y!=y.shift()
这一步很好理解,不再赘述。
>>> y != y.shift()
0 True
1 True
2 False
3 False
4 False
5 True
6 False
7 True
8 False
10 False
11 False
12 False
13 False
14 False
15 False
16 False
17 False
18 True
19 False
Name: is_decline_num, dtype: bool
Step 2: (y != y.shift()).cumsum()
>>> (y != y.shift()).cumsum()
0 1
1 2
2 2
3 2
4 2
5 3
6 3
7 4
8 4
10 4
11 4
12 4
13 4
14 4
15 4
16 4
17 4
18 5
19 5
Name: is_decline_num, dtype: int32
Step 3: y.groupby((y != y.shift()).cumsum()).cumcount()
>>> y.groupby((y != y.shift()).cumsum()).cumcount()
0 0
1 0
2 1
3 2
4 3
5 0
6 1
7 0
8 1
10 3
11 4
12 5
13 6
14 7
15 8
16 9
17 10
18 0
19 1
dtype: int64
这一步是比较关键的,首先理解y.groupby((y != y.shift()).cumsum())
,要理解此,运行y.groupby((y != y.shift()).cumsum()).sum()
会比较清楚。
>>> y.groupby((y != y.shift()).cumsum()).sum()
is_decline_num
1 0
2 4
3 0
4 11
5 0
Name: is_decline_num, dtype: int32
要理解上述结果,需要参照下述DataFrame
,以shift_cumsum
列来分组,对相应y
列的值做加和。
>>> pd.DataFrame(data = {'y':y.values, 'shift_cumsum': (y != y.shift()).cumsum().values})
y shift_cumsum
0 0 1
1 1 2
2 1 2
3 1 2
4 1 2
5 0 3
6 0 3
7 1 4
9 1 4
10 1 4
11 1 4
12 1 4
13 1 4
14 1 4
15 1 4
16 1 4
17 1 4
18 0 5
19 0 5
最关键的就是cumcount()
的理解,按照帮助文档中的叙述**Number each item in each group from 0 to the length of that group - 1.**此句话的意思是:
1、自上而下对group
数数
2、group
的产生是以(y != y.shift()).cumsum()
的生成结果为分组原则,对y
进行分组;然后利用cumcount
对每个分组中y
的值,自上而下数数(从0
开始计数)。
Step 4: +1
和y*
+1
:确保连续下降是从1开始计数
y*
:因为原始y
就是0
和1
两个数值,相乘便仅剩下了下降的连续度。