【问题标题】:counting the occurrences of a specific label in a sliding window计算滑动窗口中特定标签的出现次数
【发布时间】:2020-04-06 09:34:36
【问题描述】:

我有以下 DataFrame。

         t_msec    ID
0           1.1  0200
1           1.4  020a
2           8.9  01f4
3          11.1  0200
4          13.2  02e2
...         ...   ...
85454  189915.3  02e4
85455  189915.6  02e6
85456  189921.8  0200
85457  189922.3  01f4
85458  189924.0  020a

我想运行一个滑动窗口,它会提前 1 秒计算特定标签的出现次数。

only_id_df = df[df.ID == id]
counts = Counter()
for index, row in only_id_df.iterrows():
    mask = (only_id_df.t_msec< row.t_msec + 1000) & (only_id_df.t_msec> row.t_msec)
    counts.update([len(only_id_df[mask])])

但是,这非常慢,我知道必须有一种方法可以在不手动迭代每一行和过滤的情况下执行它(我知道这是低效的)。

实现这一目标的正确方法是什么?如何加快计算速度?

【问题讨论】:

    标签: python pandas dataframe data-manipulation


    【解决方案1】:

    对于我的测试,我使用了以下 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 ].

    要计算整个结果,我们必须执行一些技巧:

    1. 为了根据时间序列执行滚动计算, 我们必须计算一个临时列,涉及 pd.to_datetime 和 将其设置为索引。

    2. 但滚动计算是从 向后 当前行,即 Pandas 查找例如之间的行1 秒 之前 当前索引和 now(当前索引)并执行 在此窗口内的行上定义计算,而我们想要一个 前向窗口。 所以这里需要的技巧是:

      • 将索引计算为 df.t_msec.max() - df.t_msec [ms],
      • 以相反的顺序处理它,
      • 再次颠倒结果的顺序。
    3. 最后一招的原因是:

      • 我们要查看ID列(一个字符串),
      • 但滚动计算只能在 numeric 列上执行。
    4. 幸运的是,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
    

    执行时间应该会大大缩短。写一条关于你和我的代码执行时间的评论(+行号)。

    【讨论】:

      猜你喜欢
      • 2015-04-13
      • 1970-01-01
      • 2018-08-27
      • 1970-01-01
      • 1970-01-01
      • 2020-07-05
      • 2019-05-07
      • 2022-12-22
      • 2023-01-29
      相关资源
      最近更新 更多