基于月份限制拆分Pandas DataFrame时长数据并生成新行
拆分跨月时长至单月范围的Pandas实现
需求说明
给定包含起始时间(datetime列)和小时级时长(duration列)的Pandas DataFrame,需将跨月的时长拆分为单月内的独立记录:若某条记录的时长超出起始时间所在月份的剩余小时数,则生成新行,新行起始时间设为次月零点,剩余时长填入对应行。
示例数据
原始DataFrame
datetime duration 0 20-01-2001 03:00 960 1 01-05-2001 19:00 18
目标输出DataFrame
datetime duration 0 2001-01-20 03:00:00 285 1 2001-02-01 00:00:00 672 2 2001-03-01 00:00:00 3 3 2001-05-01 19:00:00 18
实现代码
import pandas as pd from dateutil.relativedelta import relativedelta # 构建原始数据 df = pd.DataFrame({ 'datetime': ['20-01-2001 03:00', '01-05-2001 19:00'], 'duration': [960, 18] }) # 转换datetime列为标准时间格式(注意原始格式是日-月-年) df['datetime'] = pd.to_datetime(df['datetime'], format='%d-%m-%Y %H:%M') def split_monthly_records(row): start_time = row['datetime'] remaining_hours = row['duration'] output_records = [] while remaining_hours > 0: # 计算次月零点时间,以此为边界计算当月剩余小时数 next_month_start = (start_time + relativedelta(months=1)).replace(day=1, hour=0, minute=0) available_hours = (next_month_start - start_time).total_seconds() / 3600 if remaining_hours <= available_hours: # 剩余时长未跨月,直接记录 output_records.append({ 'datetime': start_time, 'duration': remaining_hours }) remaining_hours = 0 else: # 时长跨月,先记录当月可容纳的时长,更新剩余时长和起始时间 output_records.append({ 'datetime': start_time, 'duration': available_hours }) remaining_hours -= available_hours start_time = next_month_start return output_records # 应用拆分函数并展开结果 result_df = df.apply(split_monthly_records, axis=1).explode().apply(pd.Series).reset_index(drop=True) # 将时长转为整数(匹配示例输出格式) result_df['duration'] = result_df['duration'].astype(int) print(result_df)
关键逻辑说明
- 时间格式转换:确保
datetime列是Pandas可识别的datetime64类型,后续时间计算依赖此类型。 - 单条记录拆分循环:
- 计算当前起始时间到当月结束的剩余小时数(以次月零点为边界)。
- 根据剩余时长与当月可用小时数的大小关系,决定是直接记录还是拆分出当月部分,更新剩余时长和起始时间后继续循环。
- 结果展开:用
explode将每行生成的记录列表展开为多行,最终整理成目标DataFrame格式。
内容的提问来源于stack exchange,提问作者oben
相关产品推荐
相关产品推荐

