如何对Pandas DataFrame按时间间隔重采样并按比例分配数值?
按小时间隔拆分带起止时间的Pandas数据并按比例分配数值
原始数据
我有如下Pandas DataFrame:
| index | start_time | end_time | amount |
|---|---|---|---|
| foo | 2023-03-11 09:45:27 | 2023-03-11 09:58:39 | 48 |
| bar | 2023-03-11 09:59:00 | 2023-03-11 10:09:00 | 20 |
需求与逻辑
希望按小时间隔拆分数据,得到如下结果:
| interval | amount |
|---|---|
| 2023-03-11 09:00-10:00 | 50 |
| 2023-03-11 10:00-11:00 | 18 |
逻辑说明:
- bar的1分钟(占自身总时长的10%)落在09:00-10:00区间,因此将bar的amount的10%加入该区间,即48 + 20*0.1 = 50
- bar剩余的amount(18)分配至10:00-11:00区间
现有方法的局限
我了解到resample方法适合按间隔拆分,但存在局限:
resample针对单个时间点,而非起止时间范围- 无法自动基于时间间隔的重叠比例分配数值
解决方案
Pandas没有直接内置的一键方法,但可以通过以下高效步骤实现,无需复杂自定义逻辑:
步骤1:转换时间列为datetime类型
首先确保时间列是datetime格式,方便后续计算:
import pandas as pd # 构造原始数据 df = pd.DataFrame({ 'index': ['foo', 'bar'], 'start_time': ['2023-03-11 09:45:27', '2023-03-11 09:59:00'], 'end_time': ['2023-03-11 09:58:39', '2023-03-11 10:09:00'], 'amount': [48, 20] }).set_index('index') # 转换为datetime类型 df['start_time'] = pd.to_datetime(df['start_time']) df['end_time'] = pd.to_datetime(df['end_time'])
步骤2:生成所有涉及的小时区间
提取数据覆盖的所有小时起始点,生成对应的小时区间:
# 获取覆盖的所有小时起始点 all_hours = pd.date_range( start=df['start_time'].min().floor('H'), end=df['end_time'].max().ceil('H'), freq='H' ) # 构造小时区间的起止时间 hour_intervals = pd.DataFrame({ 'interval_start': all_hours[:-1], 'interval_end': all_hours[1:] })
步骤3:计算每条记录与小时区间的重叠时长
通过交叉合并,匹配每条记录和所有小时区间,计算重叠时间:
# 交叉合并原始数据与小时区间 cross = df.assign(key=1).merge(hour_intervals.assign(key=1), on='key').drop('key', axis=1) # 计算重叠时段的起止时间 cross['overlap_start'] = cross[['start_time', 'interval_start']].max(axis=1) cross['overlap_end'] = cross[['end_time', 'interval_end']].min(axis=1) # 计算重叠时长(秒),过滤无重叠的记录 cross['overlap_seconds'] = (cross['overlap_end'] - cross['overlap_start']).dt.total_seconds() cross = cross[cross['overlap_seconds'] > 0]
步骤4:按比例分配amount并汇总
根据重叠时长占比分配amount,最后按区间汇总结果:
# 计算每条记录的总时长 cross['total_seconds'] = (cross['end_time'] - cross['start_time']).dt.total_seconds() # 计算当前区间应分配的amount cross['allocated_amount'] = cross['amount'] * (cross['overlap_seconds'] / cross['total_seconds']) # 格式化区间名称并汇总求和 result = cross.groupby( cross['interval_start'].dt.strftime('%Y-%m-%d %H:00') + '-' + cross['interval_end'].dt.strftime('%H:00') )['allocated_amount'].sum().round().reset_index(name='amount')
执行后得到的result即为目标结果:
interval amount 0 2023-03-11 09:00-10:00 50 1 2023-03-11 10:00-11:00 18
补充说明
这个方法的核心是交叉匹配时间区间+计算重叠比例,可扩展到任意时间间隔(如15分钟、日度等),只需修改date_range的freq参数即可。利用Pandas向量运算,效率高于自定义循环逻辑,也更易维护。
内容的提问来源于stack exchange,提问作者PlankTon
相关产品推荐
相关产品推荐

