如何高效统计多列各日期区间行数?求替代现有方法的优化方案
需求与优化方案
需求背景
用户持有一个Pandas DataFrame,列对应流程的各个阶段,每行记录该行数据进入对应阶段的日期。核心需求:
- 统计每个阶段(列)中日期落在指定区间内的行数
- 最终用于绘制漏斗图分析阶段流失情况,同时对比不同日期区间的漏斗差异
最初错误的尝试代码
用户最初的代码逻辑有误,会错误统计跨区间的行,代码如下:
start = pd.to_datetime('2023-1-1') end = pd.to_datetime('2023-3-30') df[(df[list_of_cols] >= start).any(axis=1)) & (df[list_of_columns] <= end).any(axis=1))] df[list_of_cols].count()
现有可行但繁琐的实现
用户编写了funnel_by_time函数可实现需求,但嵌套循环逻辑较繁琐,代码如下:
def funnel_by_time(df: pd.DataFrame, dates: list): df_dict = {date.strftime('%m/%d/%Y'): [] for date in dates[:-1]} # 为每个时间段创建统计列表 for i in range(len(dates)-1): date_range = pd.date_range(dates[i], dates[i+1]) # 统计每个阶段在当前日期区间内的行数 for col in stage_cols: df_dict[dates[i].strftime('%m/%d/%Y')].append( df[col].isin(date_range).sum()) return pd.DataFrame.from_dict(df_dict, orient='index', columns=stage_cols)
更简洁高效的优化方案
利用Pandas的向量化操作替代嵌套循环,既能提升运行效率,又能简化代码逻辑:
方案1:基于cut的分组统计
def funnel_by_time_optimized(df: pd.DataFrame, dates: list): # 将日期区间映射为起始日期标签 time_labels = [d.strftime('%m/%d/%Y') for d in dates[:-1]] # 对所有阶段的日期做区间划分 time_bins = pd.cut(df[stage_cols].stack(), bins=dates, labels=time_labels, include_lowest=True) # 分组统计各时间段、各阶段的有效行数 result = (df[stage_cols] .stack() .reset_index(name='date') .assign(time_period=time_bins) .dropna(subset=['date']) .groupby(['time_period', 'level_1'])['date'] .count() .unstack() .fillna(0) .astype(int)) return result
方案2:向量化区间判断
如果不想使用cut,可以直接对每个区间做向量化的日期判断:
def funnel_by_time_vectorized(df: pd.DataFrame, dates: list): result = pd.DataFrame() for start, end in zip(dates[:-1], dates[1:]): # 对每个阶段列,统计日期在[start, end]范围内的行数 counts = df[stage_cols].apply(lambda col: col.between(start, end).sum()) result.loc[start.strftime('%m/%d/%Y'), :] = counts return result.astype(int)
方案优势
- 依赖Pandas原生向量化操作,避免嵌套循环,数据量越大效率提升越明显
- 代码逻辑更直观,可读性更强
- 自动处理缺失日期的情况,无需额外判断逻辑
内容的提问来源于stack exchange,提问作者Charles Lyman
相关产品推荐
相关产品推荐

