pandas如何基于起止日期创建逐小时值映射的日历DataFrame
问题背景
现有存储业务记录的DataFrame,结构如下:
Name First_date End_date Value 1 AAA 2022-06-18 00:00:00 2022-06-18 20:00:00 20 2 BBB 2022-06-19 14:00:00 2022-06-19 16:00:00 40 87 CCC 2022-06-18 04:00:00 2022-06-20 04:00:00 60 0 DDD 2022-06-18 00:00:00 2022-06-20 23:00:00 60 93 EEE 2022-06-26 05:30:00 2022-06-26 15:30:00 35 91 FFF 2022-06-18 00:00:00 2022-06-26 23:00:00 32 230 GGG 2022-06-18 00:00:00 2022-07-02 00:00:00 36 4265 HHH 2022-06-18 00:00:00 2022-07-02 18:00:00 40
另有预生成的空DataFrame,列是2022-06-18 00:00:00到2023-01-01的全部小时级时间点,共4705列。
需要实现的逻辑:
- 结果表行索引使用源数据的
Name字段 - 对每一列(对应一个小时级时间点),若时间点落在某条记录的
First_date和End_date区间内,将对应Value填入单元格,未命中则填0
预期输出样例:
Index 2022-06-18 00:00:00 2022-06-18 01:00:00 2022-06-18 02:00:00 ... AAA 20 20 20 BBB 0 0 0 CCC 0 0 0 DDD 60 60 60 EEE 0 0 0 FFF 32 32 32 GGG 36 36 36 HHH 40 40 40
此前尝试使用groupby搭配agg(sum)实现,未得到正确结果。
解决方案
直接用NumPy广播做批量区间判断即可,不需要逐行循环、也不需要先把区间展开成长表再聚合,在4700列的场景下运行效率远高于apply或groupby方案,且边界判断准确。
首先确保源数据的时间列已转换为datetime类型,避免字符串比较出错:
import pandas as pd import numpy as np # 源数据时间列格式转换 df["First_date"] = pd.to_datetime(df["First_date"]) df["End_date"] = pd.to_datetime(df["End_date"]) # 若已经预生成了空的小时级DataFrame,直接取其columns即可,无需重复生成 time_columns = pd.date_range(start="2022-06-18 00:00:00", end="2023-01-01", freq="H") # 批量生成布尔判断矩阵:形状为(源数据行数, 时间点数量),值为True代表该时间点落在对应记录的时间区间内 in_range_mask = (time_columns.values >= df["First_date"].values[:, np.newaxis]) & \ (time_columns.values <= df["End_date"].values[:, np.newaxis]) # 命中区间的位置填充对应Value,未命中填0 result_values = np.where(in_range_mask, df["Value"].values[:, np.newaxis], 0) # 组装为最终结果表 result_df = pd.DataFrame( data=result_values, index=df["Name"], columns=time_columns )
关于之前groupby+agg(sum)方案失效的说明
这类方案的常规思路是先把每条记录的时间区间拆成对应的小时级时间点,构造成长表后按Name+时间点聚合求和。失效通常是两个原因:
- 拆分时间区间时没有对齐整点规则,非整点的起止时间(比如EEE的05:30起始、HHH的18:00结束)处理错误,把不在区间内的小时点算入了范围
- 拆分长表后没有去重,出现同一条记录对应同一个时间点多次计数的问题
上面的广播方案直接做时间值的大小比较,不需要做区间拆分,天然规避了这两个问题。如果需要调整区间规则(比如改成左闭右开),直接修改判断条件里的比较运算符即可。
内容的提问来源于stack exchange,提问作者nicolax9777
相关产品推荐
相关产品推荐

