如何高效统计pandas DataFrame中日期落在Start、End列间的不同Category数量
最高效实现方案
你原来的循环写法时间复杂度为O(M*N)(M为日期总数,N为原始df行数),数据量增大后性能会非常差,推荐使用事件扫描法,时间复杂度仅为O(N log N),完全不需要逐天遍历过滤:
import pandas as pd import datetime # 1. 准备原始数据 d = pd.date_range(start='2015-01-01', end='2015-12-31', freq='D') df = pd.DataFrame({ 'Category': ['a', 'a', 'b', 'b', 'c'], 'Start': [datetime.date(2014,12,1), datetime.date(2015,10,2), datetime.date(2015,10,1), datetime.date(2015,10,1), datetime.date(2015,6,1)], 'End': [datetime.date(2015,6,1), datetime.date(2015,10,16), datetime.date(2016,4,1), datetime.date(2015,12,1), datetime.date(2015,8,1)] }) # 统一转成pandas datetime类型避免类型匹配错误 df[['Start', 'End']] = df[['Start', 'End']].apply(pd.to_datetime) # 2. 每个Category先合并重叠/相邻区间,避免重复计数 def merge_intervals(group): group = group.sort_values('Start') merged = [] for _, row in group.iterrows(): if not merged: merged.append([row['Start'], row['End']]) else: last_start, last_end = merged[-1] if row['Start'] <= last_end: merged[-1][1] = max(last_end, row['End']) else: merged.append([row['Start'], row['End']]) return pd.DataFrame(merged, columns=['Start', 'End']) merged_df = df.groupby('Category', group_keys=False).apply(merge_intervals).reset_index(drop=True) # 3. 生成事件列表:区间开始日期计数+1,区间结束后一天计数-1 events = [] for _, row in merged_df.iterrows(): events.append({'date': row['Start'], 'delta': 1}) events.append({'date': row['End'] + pd.Timedelta(days=1), 'delta': -1}) event_df = pd.DataFrame(events) # 4. 按日期聚合事件,计算累计活跃分类数 event_df = event_df.groupby('date')['delta'].sum().reset_index() event_df = event_df.sort_values('date') event_df['count'] = event_df['delta'].cumsum() # 5. 对齐到目标日期范围,填充缺失值 df_count = pd.DataFrame({'date': d}).merge(event_df[['date', 'count']], on='date', how='left') df_count['count'] = df_count['count'].ffill().fillna(0).astype(int) df_count = df_count.set_index('date')
输出的df_count和你预期的格式完全一致,当原始数据有十万行、日期范围跨度长达数年时,性能比原循环写法高出数百倍。
内容的提问来源于stack exchange,提问作者Olba12
相关产品推荐
相关产品推荐

