Pandas含重叠分组/窗口的groupby适配性及替代方案问询
解决跨天事件多分组的替代方案
这个问题我之前也踩过坑!普通的groupby确实没法让跨天事件同时归属到多个日期分组里,核心思路是先把每个事件拆分成它覆盖的所有日期的独立行,之后再用groupby就完全符合需求了。
我先补全你的示例数据,方便演示:
import pandas as pd # 构造每6小时一个的事件,必然会有跨天的情况 start = pd.DatetimeIndex(start='2018-02-01 21:00:00', end='2018-02-05 21:00:00', freq='6h') df = pd.DataFrame({ 'start': start, 'end': start.shift(1), # 每个事件持续6小时 'event_id': range(len(start)) # 给事件加ID方便跟踪 }) df = df.dropna() # 移除最后一行的空值(shift生成的)
第一步:生成每个事件覆盖的所有日期
我们需要给每个事件生成它涉及的所有日期(从事件开始的日期到结束的日期),用apply结合pd.date_range就能实现:
# 为每行事件生成覆盖的日期列表 df['covered_dates'] = df.apply( lambda row: pd.date_range(start=row.start.date(), end=row.end.date(), freq='D'), axis=1 )
比如一个从2018-02-01 21:00到2018-02-02 03:00的事件,covered_dates会包含2018-02-01和2018-02-02两个日期。
第二步:拆分事件为多行
用explode方法把每个事件拆分成对应日期的独立行,这样跨天事件就会出现在所有它涉及的日期分组里:
expanded_df = df.explode('covered_dates')
现在看expanded_df,原本的跨天事件会变成两行,分别对应两个日期,完全满足你的需求。
第三步:正常分组并应用函数
现在就可以像普通分组一样操作了,比如统计每天的独立事件数:
# 按日期分组,统计每天的事件数量 daily_event_count = expanded_df.groupby('covered_dates')['event_id'].nunique()
如果需要更精细的计算(比如每个事件在当天的实际贡献时长),可以写个自定义函数应用到每行:
def get_daily_duration(row): # 事件的真实起止时间 event_start = row.start event_end = row.end # 当前行对应的日期 current_day = row.covered_dates # 当天的时间范围(00:00到次日00:00) day_start = pd.Timestamp(current_day) day_end = day_start + pd.Timedelta(days=1) # 计算事件与当天的重叠时长 overlap_start = max(event_start, day_start) overlap_end = min(event_end, day_end) return (overlap_end - overlap_start).total_seconds() / 3600 # 转换为小时 # 计算每行的当日贡献时长 expanded_df['daily_duration'] = expanded_df.apply(get_daily_duration, axis=1) # 按日期求和,得到每天的总事件时长 daily_total_duration = expanded_df.groupby('covered_dates')['daily_duration'].sum()
为什么这个方法比直接groupby好?
普通groupby只能把事件归到单一分组(比如start日期或end日期),而这种拆分方法让跨天事件同时属于所有它覆盖的日期,完全匹配你的业务需求。而且拆分后的DataFrame可以复用所有pandas的分组操作,灵活性拉满。
内容的提问来源于stack exchange,提问作者bnaul
相关产品推荐
相关产品推荐

