如何计算一系列间隔中1小时间隔的总数



让我们考虑排序时间间隔的以下数据帧:

import pandas as pd
from io import StringIO
s="""start_time,end_time
2022-01-01 12:30:00,2022-01-01 12:45:00
2022-01-01 13:05:00,2022-01-01 13:50:00
2022-01-01 14:00:00,2022-01-01 14:20:00
2022-01-01 16:00:00,2022-01-01 16:45:00
2022-01-01 17:20:00,2022-01-01 17:35:00
2022-01-01 17:45:00,2022-01-01 18:30:00
2022-01-01 19:00:00,2022-01-01 19:25:00"""
df = pd.read_csv(StringIO(s), sep=",")
df.start_time = pd.to_datetime(df.start_time)
df.end_time = pd.to_datetime(df.end_time)
start_time               end_time
0    2022-01-01 12:30:00    2022-01-01 12:45:00
1    2022-01-01 13:05:00    2022-01-01 13:50:00
2    2022-01-01 14:00:00    2022-01-01 14:20:00
3    2022-01-01 16:00:00    2022-01-01 16:45:00
4    2022-01-01 17:20:00    2022-01-01 17:35:00
5    2022-01-01 17:45:00    2022-01-01 18:30:00
6    2022-01-01 19:00:00    2022-01-01 19:25:00

其思想是,1小时的间隔基本上按以下方式计算:我们从第一个区间的start_time开始,再加上1小时

如果生成的时间戳在数据帧中的以下某个时间间隔内,则我们通过向该新时间戳添加1小时来重复该过程,依此类推

但是,如果生成的时间戳不在两个时间间隔内,而是在之间,则我们继续将1小时添加到下一个时间间隔的start_time

输入将是上面的数据帧。

过程是:我们首先在第一个区间的start_time上加1小时:

  1. 12:30+1H->13:30(13:30是一个在可用间隔内的时间戳。特别是,它在13:05-13:50之间,这是我们数据帧中的一个间隔。然后,我们将从13:30开始继续(。

  2. 13:30+1H->14:30(14:30不包含在我们的任何df区间中-我们在14:30之后选择最接近的start_time(

  3. 16:00+1H->17:00(17:00不包括在我们数据帧的任何间隔中(

  4. 17:20+1H->18:20(18:20包含在17:45-18:30之间,这也是我们数据帧中的间隔(

  5. 18:20+1H->19:20(包含在我们的最后一个区间(

  6. 19:20+1H->20:20(我们已经达到或超过(大于或等于(上一个整数的end_time,所以我们停止(例如,如果数据帧中的最后一个end_time是19:20:00而不是19:25:00,那么我们将在前一步中停止(因为我们到达的时间戳大于或等于最后的end_time(

输出:6(在最后一个end_time等于19:20:00的替代情况下,输出将等于5(。

输出代表添加1H的过程被重复的总次数。

就代码而言,我曾想过可能以某种方式使用.shift(),但我不确定如何使用。问题是,当得到的时间戳不在可用间隔之间时,我们应该搜索最接近的start_time

矢量化(即并行化(不太可能实现,因为每一步的过程都取决于前一步的计算结果。在任何情况下,解决方案都将是某种迭代。工作的速度将主要取决于你选择使用的算法。

在我看来,一个好的算法是查看相邻记录的end_timestart_time是否落入相同的小时步长,就好像我们从某个点开始按小时测量长度一样。为此,我们可以使用整数除法:

import pandas as pd
from io import StringIO
s = """start_time,end_time
2022-01-01 12:30:00,2022-01-01 12:45:00
2022-01-01 13:05:00,2022-01-01 13:50:00
2022-01-01 14:00:00,2022-01-01 14:20:00
2022-01-01 16:00:00,2022-01-01 16:45:00
2022-01-01 17:20:00,2022-01-01 17:35:00
2022-01-01 17:45:00,2022-01-01 18:30:00
2022-01-01 19:00:00,2022-01-01 19:25:00"""
df = pd.read_csv(StringIO(s), parse_dates=[0, 1])
data = df.to_numpy().flatten()
start = data[0]
step = pd.Timedelta(1, 'H')   # hour as a unit of length
count = 0
for x, y in data[1:-1].reshape(-1, 2):
# x is previous end_time
# y is next start_time
length = (x-start) // step + 1
if start + step*length < y:
count += length
start = y
integer, decimal = divmod((data[-1] - start) / step, 1)
count += integer if decimal == 0 else integer+1
print(f'{count = }')

不确定这里是否真的需要panda,但这里有一个遵循您逻辑的解决方案。

from datetime import timedelta
import numpy as np
count = 0
start = df.loc[0,'start_time']
while 1:
count += 1
print("hour interval start:", start)
end_of_interv = start + timedelta(hours=1)
new_row = np.searchsorted(df.end_time, end_of_interv)
if new_row >= len(df):
break
s, e = df.loc[new_row, ['start_time', 'end_time']]
if end_of_interv < s:
start = s
elif s < end_of_interv < e:
start = end_of_interv
print("Number of intervals counted: %d" % count)
#hour interval start: 2022-01-01 12:30:00
#hour interval start: 2022-01-01 13:30:00
#hour interval start: 2022-01-01 16:00:00
#hour interval start: 2022-01-01 17:20:00
#hour interval start: 2022-01-01 18:20:00
#hour interval start: 2022-01-01 19:20:00
#Number of intervals counted: 6

你应该在几个不同间隔(例如,一些超过1小时(和开始时间的例子上测试这一点,并验证它能产生你想要的答案。

使用start_time作为索引允许使用pandas.Index.get_indexer方法,这样可以更容易地搜索下一个起点。这里,是一个基于它的解决方案

import pandas as pd
import numpy as np
from io import StringIO
s = """start_time,end_time
2022-01-01 12:30:00,2022-01-01 12:45:00
2022-01-01 13:05:00,2022-01-01 13:50:00
2022-01-01 14:00:00,2022-01-01 14:20:00
2022-01-01 16:00:00,2022-01-01 16:45:00
2022-01-01 17:20:00,2022-01-01 17:35:00
2022-01-01 17:45:00,2022-01-01 18:30:00
2022-01-01 19:00:00,2022-01-01 19:25:00"""
def is_in_range(var):
# check if the given timestamp exists withing any of the range in the dataframe
in_range = df.loc[(df['start_time']<=var) & (df['end_time']> var)].shape[0]
# increment by one hour if it exists
if in_range:
var = var + pd.Timedelta(1,'H')

# If not, find the next closest start time (=index)
else:
var = df.index[df.index.get_indexer([var],'bfill')][0]
return var, in_range
df = pd.read_csv(StringIO(s))
df[["start_time", "end_time"]] = df[["start_time", "end_time"]].apply(pd.to_datetime)

glb_start_time = df.loc[0,'start_time']
glb_end_time = df.loc[df.index[-1], 'end_time']
df.index= df['start_time']
count = 0
while glb_start_time< glb_end_time:
glb_start_time, in_range = is_in_range(glb_start_time)
if in_range:
count += 1
print(count)
def update_df(frame, check_time, hour):
# returns: new_check_time, count
# updates the data frame

frame = frame[frame['end_time'] > check_time] 
if frame.shape[0] == 0 : return None, 0 # exceeds the latest time

filt = frame['start_time'] <= check_time
frame = frame[~filt].reset_index(drop=True)
if filt[filt].any(): return check_time + hour, 1 # it is insdie interval
else: return frame.loc[0, 'start_time'] + hour, 1 # it is not in the interval

hour = pd.to_timedelta(1, 'h')    
check_time = df.loc[0, 'start_time'] + hour
total_count = 1
while True:
new_check_time, count = update_df(df, check_time, hour)

total_count += count
if new_check_time is None: break
else: check_time = new_check_time

print(total_count)

最新更新