查找一天中事件的开始时间和结束时间 - Pandas 时间序列 - 这样结束时间不会落入第二天

Posted

技术标签:

【中文标题】查找一天中事件的开始时间和结束时间 - Pandas 时间序列 - 这样结束时间不会落入第二天【英文标题】:Finding start-time and end-time of events in a day - Pandas timeseries - such that end time does not fall into next day 【发布时间】:2021-07-23 17:55:02 【问题描述】:

我有一个气象时间序列df:

df = pd.DataFrame('date':['11/10/2017 0:00','11/10/2017 03:00','11/10/2017 06:00','11/10/2017 09:00','11/10/2017 12:00',
                       '11/11/2017 0:00','11/11/2017 03:00','11/11/2017 06:00','11/11/2017 09:00','11/11/2017 12:00',
                      '11/12/2017 00:00','11/12/2017 03:00','11/12/2017 06:00','11/12/2017 09:00','11/12/2017 12:00'],
              'value':[850,np.nan,np.nan,np.nan,np.nan,500,650,780,np.nan,800,350,690,780,np.nan,np.nan])
df['date'] = pd.to_datetime(df.date.astype(str), format='%m/%d/%Y %H:%M',errors ='coerce') 
df.index = pd.DatetimeIndex(df.date)

通过这个数据框,我试图找出事件开始时间结束时间

(df["value"] < 1000)

我使用了类似于How to find the start time and end time of an event in python?的解决方案 修改后的代码:

current_event = None
result = []
for event, time in zip((df["value"] < 1000), df.index):
    if event != current_event:
        if current_event is not None:
            result.append([current_event, start_time, time - pd.DateOffset(hours = 1, minutes = 30)])
        current_event, start_time = event, time - pd.DateOffset(hours = 1, minutes = 30)
df = pd.DataFrame(result, columns=['Event','StartTime','EndTime'])
df

输出是

   Event           StartTime             EndTime
0   True 2017-11-09 22:30:00 2017-11-10 01:30:00
1  False 2017-11-10 01:30:00 2017-11-10 22:30:00
2   True 2017-11-10 22:30:00 2017-11-11 07:30:00
3  False 2017-11-11 07:30:00 2017-11-11 10:30:00
4   True 2017-11-11 10:30:00 2017-11-12 07:30:00

期望的输出是:

所需的输出与上面的输出不同:

    第二行(索引 1)中的

    EndTime2017-11-10 13:30:00

    EndTime 第五行(索引 4 )为 2017-11-11 13:30:00

    新行第六行(索引 5)和第 6 行

逻辑:

由于时间戳相隔 3 小时,因此假设事件在时间戳前 1 小时 30 分钟开始,在时间戳后 1 小时 30 分钟结束。

如果两个连续事件相似,则它们加起来如下:第一个时间戳之前的 1 小时 30 分钟到第二个时间戳之后的 1 小时 30 分钟,依此类推。

一天中第一个事件的开始时间,即时间 00:00 应始终为 00:00 时间戳前 1 小时 30 分钟,即前一天的 22:30。

一天中最后一个事件的结束时间,即 12:00 应该始终是 12:00 时间戳之后 1 小时 30 分钟,即同一天的 13:30。

对于这个问题的任何及时帮助将不胜感激。试图拼命修复它,但还没有成功。

非常感谢!

【问题讨论】:

你确定你已经分享了你最新的代码吗?因为我在time - pd.DateOffset(hours = 1, minutes = 30) 的第一个实例周围得到TypeError: unsupported operand type(s) for -: 'int' and 'datetime.timedelta' 代码是最新的。首先,您需要将索引转换为 datetimeindex,如下所示: df['date'] = pd.to_datetime(df.date.astype(str), format='%m/%d/%Y %H:%M', errors ='coerce') df.index = pd.DatetimeIndex(df.date) df.drop('date', axis = 1, inplace = True) 酷,继续把代码编辑到你的原始代码块中,我会尝试再次运行它 @Kevin ,原代码块中已经编辑了代码.. 【参考方案1】:

我不知道 numpy 是否对此有一个很好的有效解决方案,但我可以想出一种方法来使用常规 Python 类型。您现有的代码在按事件类型对测量进行分组方面做得很好,但是当测量相隔超过三个小时时,您似乎也希望将组分开。使用类似于itertools.groupby 的方法,这不是难做到的。我将我的实现分离到它自己的函数中,这样更容易与您的业务逻辑分开。

import pandas as pd
import numpy as np
import itertools

def groupby_similar(seq, key, delta):
    """like itertools.groupby, but puts two values into the same group as long as their difference is less than or equal to delta."""
    no_item = object()
    prev_item = no_item
    group = []
    for item in seq:
        if prev_item is no_item or key(item) - delta <= key(prev_item):
            group.append(item)
        else:
            yield group
            group = [item]
        prev_item = item
    if group:
        yield group

df = pd.DataFrame('date':['11/10/2017 0:00','11/10/2017 03:00','11/10/2017 06:00','11/10/2017 09:00','11/10/2017 12:00',
                       '11/11/2017 0:00','11/11/2017 03:00','11/11/2017 06:00','11/11/2017 09:00','11/11/2017 12:00',
                      '11/12/2017 00:00','11/12/2017 03:00','11/12/2017 06:00','11/12/2017 09:00','11/12/2017 12:00'],
              'value':[850,np.nan,np.nan,np.nan,np.nan,500,650,780,np.nan,800,350,690,780,np.nan,np.nan])
df['date'] = pd.to_datetime(df.date.astype(str), format='%m/%d/%Y %H:%M',errors ='coerce') 
df.index = pd.DatetimeIndex(df.date)

expected_delta = pd.DateOffset(hours = 3)
events_and_times = zip((df["value"] < 1000), df.index)
result = []
for timechunk in groupby_similar(events_and_times, key=lambda et: et[1], delta=pd.DateOffset(hours=3)):
    for event, group in itertools.groupby(timechunk, key=lambda et: et[0]):
        group = list(group)
        start_time = group[0][1]  - pd.DateOffset(hours=1, minutes=30)
        end_time   = group[-1][1] + pd.DateOffset(hours=1, minutes=30)
        result.append([event, start_time, end_time])

df = pd.DataFrame(result, columns=['Event','StartTime','EndTime'])
print(df)

结果:

   Event           StartTime             EndTime
0   True 2017-11-09 22:30:00 2017-11-10 01:30:00
1  False 2017-11-10 01:30:00 2017-11-10 13:30:00
2   True 2017-11-10 22:30:00 2017-11-11 07:30:00
3  False 2017-11-11 07:30:00 2017-11-11 10:30:00
4   True 2017-11-11 10:30:00 2017-11-11 13:30:00
5   True 2017-11-11 22:30:00 2017-11-12 07:30:00
6  False 2017-11-12 07:30:00 2017-11-12 13:30:00

我还用 itertools.groupby 替换了您在其他帖子中使用的事件分组配方,因为它更容易识别最终的 False 事件。

【讨论】:

@ Kevin ...是的,这就是我需要的结果。非常感谢您的帮助。高度赞赏。非常感谢。【参考方案2】:

创建输出数据框:

out = pd.DataFrame("Event": df["value"] < 1000,
                    "StartTime": df["date"] - pd.DateOffset(hours=1, minutes=30),
                    "EndTime": df["date"] + pd.DateOffset(hours=1, minutes=30),
                   index=df.index)
>>> out
    Event           StartTime             EndTime
0    True 2017-11-09 22:30:00 2017-11-10 01:30:00  # Group 0
1   False 2017-11-10 01:30:00 2017-11-10 04:30:00  # Group 1
2   False 2017-11-10 04:30:00 2017-11-10 07:30:00
3   False 2017-11-10 07:30:00 2017-11-10 10:30:00
4   False 2017-11-10 10:30:00 2017-11-10 13:30:00
5    True 2017-11-10 22:30:00 2017-11-11 01:30:00  # Group 2
6    True 2017-11-11 01:30:00 2017-11-11 04:30:00
7    True 2017-11-11 04:30:00 2017-11-11 07:30:00
8   False 2017-11-11 07:30:00 2017-11-11 10:30:00  # Group 3
9    True 2017-11-11 10:30:00 2017-11-11 13:30:00  # Group 4
10   True 2017-11-11 22:30:00 2017-11-12 01:30:00  # Group 5
11   True 2017-11-12 01:30:00 2017-11-12 04:30:00
12   True 2017-11-12 04:30:00 2017-11-12 07:30:00
13  False 2017-11-12 07:30:00 2017-11-12 10:30:00  # Group 6
14  False 2017-11-12 10:30:00 2017-11-12 13:30:00

定义一些辅助组:

event_group = out["Event"].ne(out["Event"].shift(fill_value=0)).cumsum()
time_group = (out["StartTime"] 
              - out["EndTime"].shift(fill_value=out["StartTime"].iloc[0])
              != pd.Timedelta(0)).cumsum()
>>> out[["Event"]].assign(EventGroup=event_group,
                          TimeGroup=time_group,
                          Groups=event_group + time_group)
    Event  EventGroup  TimeGroup  Groups
0    True           1          0       1  # Group 0
1   False           2          0       2  # Group 1
2   False           2          0       2
3   False           2          0       2
4   False           2          0       2
5    True           3          1       4  # Group 2
6    True           3          1       4
7    True           3          1       4
8   False           4          1       5  # Group 3
9    True           5          1       6  # Group 4
10   True           5          2       7  # Group 5
11   True           5          2       7
12   True           5          2       7
13  False           6          2       8  # Group 6
14  False           6          2       8

减少输出数据帧:

out = pd.DataFrame(out.groupby(event_group + time_group)
                      .apply(lambda g: (g["Event"].iloc[0],
                                        g["StartTime"].iloc[0], 
                                        g["EndTime"].iloc[-1]))
                      .tolist(), columns=["Event", "StartTime", "EndTime"])
>>> out
   Event           StartTime             EndTime
0   True 2017-11-09 22:30:00 2017-11-10 01:30:00
1  False 2017-11-10 01:30:00 2017-11-10 13:30:00
2   True 2017-11-10 22:30:00 2017-11-11 07:30:00
3  False 2017-11-11 07:30:00 2017-11-11 10:30:00
4   True 2017-11-11 10:30:00 2017-11-11 13:30:00
5   True 2017-11-11 22:30:00 2017-11-12 07:30:00
6  False 2017-11-12 07:30:00 2017-11-12 13:30:00

【讨论】:

感谢您对我的问题的关心并再次很好地解决了它。对于像我这样的初学者来说,代码简洁易懂。我非常感谢你的帮助.. @Corralien 我已经发布了一个问题,你能帮我解决这个问题吗?***.com/questions/68130863/…

以上是关于查找一天中事件的开始时间和结束时间 - Pandas 时间序列 - 这样结束时间不会落入第二天的主要内容,如果未能解决你的问题,请参考以下文章

查找一天中花费的时间以及休息时间

提取关于0值的时间,求一天中的多个工作时间,在MongoDB数组操作中

查询涉及时间维度的数仓数据

MySQL - PHP:计算一天中多个事件之间的总小时数

如何使用 Swift 获取一天中的时间?

一天中特定时间的数据库挖掘、自动图表和电子邮件