Pandas中如何拆分跨午夜的时间记录并按日计算时长
跨天时间记录拆分与每日时长计算方案
原始数据集
首先加载示例时间区间数据:
import pandas as pd df = pd.DataFrame({'Start': ['2022-01-01 08:30:00', '2022-01-01 13:00:00', '2022-01-02 22:00:00'], 'Stop': ['2022-01-01 12:00:00', '2022-01-02 10:30:00', '2022-01-04 8:00:00']}) df[['Start', 'Stop']] = df[['Start', 'Stop']].apply(pd.to_datetime)
原始数据结构:
| Start | Stop |
|---|---|
| 2022-01-01 08:30:00 | 2022-01-01 12:00:00 |
| 2022-01-01 13:00:00 | 2022-01-02 10:30:00 |
| 2022-01-02 22:00:00 | 2022-01-04 08:00:00 |
目标拆分后结构:
| Start | Stop |
|---|---|
| 2022-01-01 08:30:00 | 2022-01-01 12:00:00 |
| 2022-01-01 13:00:00 | 2022-01-02 00:00:00 |
| 2022-01-02 00:00:00 | 2022-01-02 10:30:00 |
| 2022-01-02 22:00:00 | 2022-01-03 00:00:00 |
| 2022-01-03 00:00:00 | 2022-01-04 00:00:00 |
| 2022-01-04 00:00:00 | 2022-01-04 08:00:00 |
方法1:拆分跨午夜记录,得到目标上采样结果
核心逻辑是提取每条时间区间覆盖的所有自然日0点作为切分点,和原起止时间拼接后两两配对生成新的区间,自动适配不跨天、跨1天、跨多天的所有场景:
def split_cross_day_intervals(input_df): split_records = [] for _, row in input_df.iterrows(): s = row['Start'] e = row['Stop'] # 生成区间内所有跨天的0点时间戳 midnight_points = pd.date_range( start=s.normalize() + pd.Timedelta(days=1), end=e.normalize(), freq='D' ) # 拼接所有切分节点:原开始时间 + 所有午夜点 + 原结束时间 cut_nodes = [s] + list(midnight_points) + [e] # 相邻节点配对生成新的时间区间 for i in range(len(cut_nodes) - 1): split_records.append({ 'Start': cut_nodes[i], 'Stop': cut_nodes[i+1] }) return pd.DataFrame(split_records) # 执行拆分 split_df = split_cross_day_intervals(df)
拆分完成后直接计算时长即可:
split_df['duration'] = split_df['Stop'] - split_df['Start']
方法2:直接计算每日时长(无需拆分区间,性能更优)
如果核心需求只是统计每条记录对应的每日时长,不需要保留拆分后的区间结构,可以直接通过时间交集计算,数据量较大时运行效率更高:
# 给每条原始记录加唯一标识 df['record_id'] = df.index # 生成每条记录覆盖的所有自然日 df['cover_date'] = df.apply( lambda x: pd.date_range(start=x['Start'].date(), end=x['Stop'].date(), freq='D'), axis=1 ) # 按日期展开行 daily_df = df.explode('cover_date') # 计算每条记录在对应日期的有效起止点 daily_df['day_start'] = daily_df['cover_date'] daily_df['day_end'] = daily_df['cover_date'] + pd.Timedelta(days=1) daily_df['valid_start'] = daily_df[['Start', 'day_start']].max(axis=1) daily_df['valid_end'] = daily_df[['Stop', 'day_end']].min(axis=1) # 计算当日时长 daily_df['daily_duration'] = daily_df['valid_end'] - daily_df['valid_start']
计算结果中record_id对应原始数据的行号,daily_duration就是该条记录在对应日期的累计时长。
内容的提问来源于stack exchange,提问作者Mykola Zotko
相关产品推荐
相关产品推荐

