对于我的测试,我使用了以下 DataFrame:
t_msec ID
0 60 0200
1 70 020a
2 445 01f4
3 555 0200
4 660 02e2
5 1005 0200
6 1510 02e2
7 2105 0200
8 2260 02e2
所以如果我们寻找例如ID == '0200',距离第一行 1 秒
(t_msec == 60) 向前,然后有 3 行,在
t_msec == [ 60, 555, 1005 ].
要计算整个结果,我们必须执行一些技巧:
为了根据时间序列执行滚动计算,
我们必须计算一个临时列,涉及 pd.to_datetime 和
将其设置为索引。
-
但滚动计算是从 向后
当前行,即 Pandas 查找例如之间的行1 秒 之前
当前索引和 now(当前索引)并执行
在此窗口内的行上定义计算,而我们想要一个
前向窗口。
所以这里需要的技巧是:
- 将索引计算为 df.t_msec.max() - df.t_msec [ms],
- 以相反的顺序处理它,
- 再次颠倒结果的顺序。
-
最后一招的原因是:
- 我们要查看ID列(一个字符串),
- 但滚动计算只能在 numeric 列上执行。
幸运的是,ID 列只包含 hex 字符串,可以
转换为 int。所以我们转换它并保存在一个新的(临时)列下。
第一步是执行“设置”计算:
lbl = int('0200', 16) # Label to look for (hex -> dec)
# ID converted to dec
df['ID_dec'] = df.ID.apply(lambda x: int(x, 16))
# Set index
df = df.set_index(pd.to_datetime(df.t_msec.max() - df.t_msec, unit='ms'))
所以 df 现在包含:
t_msec ID ID_dec
t_msec
1970-01-01 00:00:02.200 60 0200 512
1970-01-01 00:00:02.190 70 020a 522
1970-01-01 00:00:01.815 445 01f4 500
1970-01-01 00:00:01.705 555 0200 512
1970-01-01 00:00:01.600 660 02e2 738
1970-01-01 00:00:01.255 1005 0200 512
1970-01-01 00:00:00.750 1510 02e2 738
1970-01-01 00:00:00.155 2105 0200 512
1970-01-01 00:00:00.000 2260 02e2 738
第二(主要)步骤是计算Nr列:
df['Nr'] = df.ID_dec[::-1].rolling(window=pd.offsets.Second(1), closed='both')\
.apply(lambda grp: grp[grp == lbl].size, raw=False).astype(int)[::-1]
注意 [::-1] 反转源列和结果本身。
现在 df 包含:
t_msec ID ID_dec Nr
t_msec
1970-01-01 00:00:02.200 60 0200 512 3
1970-01-01 00:00:02.190 70 020a 522 2
1970-01-01 00:00:01.815 445 01f4 500 2
1970-01-01 00:00:01.705 555 0200 512 2
1970-01-01 00:00:01.600 660 02e2 738 1
1970-01-01 00:00:01.255 1005 0200 512 1
1970-01-01 00:00:00.750 1510 02e2 738 1
1970-01-01 00:00:00.155 2105 0200 512 1
1970-01-01 00:00:00.000 2260 02e2 738 0
最后一步是删除临时列并恢复
原索引:
df = df.drop(columns='ID_dec').reset_index(drop=True)
最终结果是:
t_msec ID Nr
0 60 0200 3
1 70 020a 2
2 445 01f4 2
3 555 0200 2
4 660 02e2 1
5 1005 0200 1
6 1510 02e2 1
7 2105 0200 1
8 2260 02e2 0
执行时间应该会大大缩短。写一条关于你和我的代码执行时间的评论(+行号)。