查找一天中事件的开始时间和结束时间 - Pandas 时间序列 - 这样结束时间就不会落入第二天
Finding start-time and end-time of events in a day - Pandas timeseries - such that end time does not fall into next day
我有一个气象时间序列 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)
使用此数据框,我试图找出 事件 [=70] 的 开始时间 和 结束时间 =]:
(df["value"] < 1000)
我使用了类似于 的解决方案
修改后的代码:
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
但是期望的输出是:
期望的输出与上面的输出不同:
EndTime 第二行(索引 1)为 2017-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天.
如能就此问题提供及时帮助,我们将不胜感激。拼命尝试修复它,但仍然没有成功。
非常感谢!
我不知道 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
我还将您在另一个 post 中使用的事件分组方法替换为 itertools.groupby
,因为它更容易识别最终的 False 事件。
创建输出数据框:
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
我有一个气象时间序列 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)
使用此数据框,我试图找出 事件 [=70] 的 开始时间 和 结束时间 =]:
(df["value"] < 1000)
我使用了类似于
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
但是期望的输出是:
期望的输出与上面的输出不同:
EndTime 第二行(索引 1)为 2017-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天.
如能就此问题提供及时帮助,我们将不胜感激。拼命尝试修复它,但仍然没有成功。
非常感谢!
我不知道 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
我还将您在另一个 post 中使用的事件分组方法替换为 itertools.groupby
,因为它更容易识别最终的 False 事件。
创建输出数据框:
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