如何删除一行中相对于另一行在特定时间内的日期时间值?

How to remove datetime values in a row that are within a certain time relative to another row?

如果我有如下数据框:

Letter Time
0 x 2021-01-01 14:00:00
1 y 2021-01-01 18:00:00
2 y 2021-01-03 14:00:00

如果时间列 (datetime) 中的值与上一行中的时间相差 14 小时以内,我将如何删除一行?

我试过使用:

from datetime import datetime, timedelta
for i, row in enumerate(df):
    if i > 0:
        if df.at[i, 'Time'] - df.at[i-1, 'Time'] < timedelta(hours=14):
            df = df.drop(i)
        else:
            pass
    else:
        pass

但我收到与行

相关的 KeyError 1

if df.at[i, 'Time'] - df.at[i-1, 'Time'] < timedelta(hours=14):

您可以使用 shift + rsub(找到连续时间之间的差异)+ div(转换为小时)创建一个布尔掩码并过滤它:

msk = df['Time'].shift().rsub(df['Time']).div(np.timedelta64(1, 'h')) > 14
out = df[msk]

输出:

  Letter                Time
2      y 2021-01-03 14:00:00

如果时间戳在较早时间戳的 14 小时内,是否删除它取决于较早时间戳是否被删除?这个答案考虑了这个问题的答案是“是”的情况。 (如果答案是“否”,那么下面测试数据的结果解决方案将只是第一个时间戳)。

设置

测试数据:

import pandas as pd

timestamps = pd.Series([0, 6,10,14,16,29,33,45,46]).apply(pd.Timedelta, unit="hours") + pd.Timestamp("2022")

timestamps 看起来像这样:

0   2022-01-01 00:00:00
1   2022-01-01 06:00:00
2   2022-01-01 10:00:00
3   2022-01-01 14:00:00
4   2022-01-01 16:00:00
5   2022-01-02 05:00:00
6   2022-01-02 09:00:00
7   2022-01-02 21:00:00
8   2022-01-02 22:00:00
dtype: datetime64[ns]

我们的目标解决方案由第 1、4、6 和 8 个时间戳组成。

解决方案

此解决方案将使用 piso(pandas 间隔集操作)包。这个想法是为每个时间戳创建一个 14 小时 window,即间隔,并迭代地删除属于较早开始的间隔的时间戳。

import piso

# sort timestamps if not already sorted
timestamps = timestamps.sort_values()

# create 14 hour windows for each timestamp.  Can be left-closed or right-closed, but not both
intervals = pd.IntervalIndex.from_arrays(timestamps, timestamps+pd.Timedelta("14h"))

# create the "disjoint adjacency matrix", which indicates pairwise if intervals are disjoint
mat = piso.adjacency_matrix(intervals, edges="disjoint")

mat 将是一个数据框,其索引和列为 timestampsmat.values 看起来像这样

array([[False, False, False,  True,  True,  True,  True,  True,  True],
       [False, False, False, False, False,  True,  True,  True,  True],
       [False, False, False, False, False,  True,  True,  True,  True],
       [ True, False, False, False, False,  True,  True,  True,  True],
       [ True, False, False, False, False, False,  True,  True,  True],
       [ True,  True,  True,  True, False, False, False,  True,  True],
       [ True,  True,  True,  True,  True, False, False, False, False],
       [ True,  True,  True,  True,  True,  True, False, False, False],
       [ True,  True,  True,  True,  True,  True, False, False, False]])

将此矩阵的对角线设置为 True

mat.iloc[range(len(mat)),range(len(mat))] = True

我们将从第一个间隔开始。从 mat 的第一行可以推断出第二个和第三个间隔需要删除。所以我们过滤掉这些区间对应的行和列,然后移动下一个区间(行)等等,直到我们到达最后一行。请注意,我们不需要检查最后一行的任何交叉点。

i = 0
while i < len(mat) -1:
    mat = mat.loc[mat.iloc[i],mat.iloc[i]]
    i+=1

结果将是一个数据框,其值都是 True。更重要的是,索引(和列)将是间隔,其左端点是在 14 小时内删除后剩余的时间戳。

pd.Series(mat.index.left) 给出

0   2022-01-01 00:00:00
1   2022-01-01 14:00:00
2   2022-01-02 05:00:00
3   2022-01-02 21:00:00
dtype: datetime64[ns]

您可以使用它来使用 pandas.Series.isin

过滤您的原始数据框