新闻中心
使用Pandas高效统计多列日期数据在指定范围内的行数

本教程旨在解决如何高效、准确地统计Pandas DataFrame中多列日期数据在特定时间范围内的行数,这在分析多阶段流程数据时尤为常见。文章将深入探讨常见的错误做法及其原因,并提供一个基于Pandas向量化操作的优化解决方案,以实现精准的日期范围筛选和计数,最终构建出适用于漏斗分析等场景的汇总表格。
背景与问题描述
在业务流程分析中,我们经常会遇到跟踪一个记录(例如客户、订单)在不同阶段进入时间的需求。数据通常以DataFrame的形式存储,其中每一列代表一个流程阶段,每个单元格记录了该记录进入该阶段的日期。我们的目标是统计在给定日期范围内,每个阶段有多少条记录的日期落入此范围,以便进行漏斗分析,比较不同阶段的转化或流失情况。
例如,给定以下DataFrame,它展示了三条记录在三个阶段的进入日期:
| stage 1 | stage 2 | stage 3 | |
|---|---|---|---|
| row 1 | 1/3/2025 | 4/3/2025 | 5/7/2025 |
| row 2 | 2/5/2025 | 2/6/2025 | 3/4/2025 |
| row 3 | 1/15/2025 | 6/3/2025 | 7/8/2025 |
我们希望得到一个汇总表,显示不同日期范围内每个阶段的计数,例如:
| stage 1 | stage 2 | stage 3 | |
|---|---|---|---|
| 1/1/2025 - 4/1/2025 | 2 | 1 | 1 |
| 4/1/2025 - 7/1/2025 | 0 | 2 | 1 |
| 7/1/2025 - 10/1/2025 | 0 | 1 | 1 |
常见误区与低效方法
初学者在处理这类问题时,常会尝试使用any(axis=1)结合日期范围筛选。例如:
import pandas as pd
# 示例数据
data = {
'stage 1': ['1/3/2025', '2/5/2025', '1/15/2025'],
'stage 2': ['4/3/2025', '2/6/2025', '6/3/2025'],
'stage 3': ['5/7/2025', '3/4/2025', '7/8/2025']
}
df = pd.DataFrame(data, index=[f'row {i+1}' for i in range(3)])
df = df.apply(pd.to_datetime) # 确保日期格式正确
start = pd.to_datetime('2025-1-1')
end = pd.to_datetime('2025-3-30')
stage_cols = ['stage 1', 'stage 2', 'stage 3']
# 错误示例
# (df[stage_cols] >= start).any(axis=1) 会检查任何列是否大于等于start
# (df[stage_cols] <= end).any(axis=1) 会检查任何列是否小于等于end
# 这种方法会错误地将满足任一条件的行全部纳入计数
filtered_df_incorrect = df[(df[stage_cols] >= start).any(axis=1) & (df[stage_cols] <= end).any(axis=1)]
print("错误筛选结果的行数:\n", filtered_df_incorrect.shape[0])
# 这种筛选方式可能导致行3被计数,即使其stage 2和stage 3的日期超出了范围,
# 因为stage 1的日期满足条件,且stage 2的日期可能满足另一个条件。
# 并且,最终我们想要的是每个阶段的独立计数,而不是基于行的整体筛选。这种方法的问题在于,它首先在行级别进行聚合(any(axis=1)),然后才进行逻辑与操作。这意味着只要一行中的任何一个阶段日期满足起始条件,且任何一个阶段日期满足结束条件,该行就会被选中。这无法实现对每个阶段日期进行独立的范围检查和计数。例如,对于row 3,stage 1的日期在范围内,但stage 2和stage 3的日期不在范围
内。如果使用上述逻辑,row 3仍可能被错误地包含在计数中。
此外,用户可能会编写一个循环函数来逐个日期范围、逐列地进行计数,如问题中提到的funnel_by_time函数。虽然这种方法能够得到正确结果,但由于使用了Python循环,在大规模数据集上性能会比较低下,不符合Pandas的向量化操作最佳实践。
优化方案:向量化日期范围筛选与计数
Pandas提供了强大的向量化操作能力,可以高效地处理这类问题。核心思想是:先对DataFrame中的每个日期进行独立的范围检查,生成布尔矩阵,然后对该布尔矩阵按列求和。
步骤1:确保日期列为datetime类型
这是进行日期比较的前提。
import pandas as pd
data = {
'stage 1': ['1/3/2025', '2/5/2025', '1/15/2025'],
'stage 2': ['4/3/2025', '2/6/2025', '6/3/2025'],
'stage 3': ['5/7/2025', '3/4/2025', '7/8/2025']
}
df = pd.DataFrame(data, index=[f'row {i+1}' for i in range(3)])
# 将所有日期列转换为datetime对象
tmp_df = df.apply(pd.to_datetime)
print("转换后的DataFrame (tmp_df):\n", tmp_df)输出:
转换后的DataFrame (tmp_df):
stage 1 stage 2 stage 3
row 1 2025-01-03 00:00:00 2025-04-03 00:00:00 2025-05-07 00:00:00
row 2 2025-02-05 00:00:00 2025-02-06 00:00:00 2025-03-04 00:00:00
row 3 2025-01-15 00:00:00 2025-06-03 00:00:00 2025-07-08 00:00:00步骤2:定义日期范围并进行元素级比较
Mistral AI
Mistral AI被称为“欧洲版的OpenAI”,也是目前欧洲最强的 LLM 大模型平台
182
查看详情
定义起始日期和结束日期,然后使用Pandas的ge()(大于等于)和le()(小于等于)方法进行元素级的布尔比较。
start_date = pd.to_datetime('2025-1-1')
end_date = pd.to_datetime('2025-3-30')
# 检查每个日期是否大于等于起始日期
ge_mask = tmp_df.ge(start_date)
print("\n大于等于起始日期的布尔矩阵 (ge_mask):\n", ge_mask)
# 检查每个日期是否小于等于结束日期
le_mask = tmp_df.le(end_date)
print("\n小于等于结束日期的布尔矩阵 (le_mask):\n", le_mask)输出:
大于等于起始日期的布尔矩阵 (ge_mask):
stage 1 stage 2 stage 3
row 1 True True True
row 2 True True True
row 3 True True True
小于等于结束日期的布尔矩阵 (le_mask):
stage 1 stage 2 stage 3
row 1 True False False
row 2 True True True
row 3 True False False步骤3:结合布尔矩阵并按列求和
将两个布尔矩阵通过逻辑与操作符&结合,生成最终的布尔矩阵,其中True表示该日期在该范围内。然后,对这个最终的布尔矩阵按列求和,即可得到每个阶段在指定日期范围内的计数。
# 结合两个布尔矩阵
final_mask = ge_mask & le_mask
print("\n最终布尔矩阵 (final_mask):\n", final_mask)
# 按列求和得到每个阶段的计数
counts = final_mask.sum()
print("\n单个日期范围内的计数结果:\n", counts)输出:
最终布尔矩阵 (final_mask):
stage 1 stage 2 stage 3
row 1 True False False
row 2 True True True
row 3 True False False
单个日期范围内的计数结果:
stage 1 3
stage 2 1
stage 3 1
dtype: int64通过这种方法,我们可以看到stage 1有3个日期在范围内,stage 2有1个,stage 3有1个。这与预期结果一致。
构建多日期范围的汇总表
为了生成漏斗分析所需的汇总表,我们需要对多个日期范围重复上述过程,并将结果收集到一个新的DataFrame中。
import pandas as pd
# 原始数据
data = {
'stage 1': ['1/3/2025', '2/5/2025', '1/15/2025'],
'stage 2': ['4/3/2025', '2/6/2025', '6/3/2025'],
'stage 3': ['5/7/2025', '3/4/2025', '7/8/2025']
}
df = pd.DataFrame(data, index=[f'row {i+1}' for i in range(3)])
tmp_df = df.apply(pd.to_datetime) # 确保日期格式正确
# 定义日期范围列表
date_ranges = [
(pd.to_datetime('2025-1-1'), pd.to_datetime('2025-4-1')),
(pd.to_datetime('2025-4-1'), pd.to_datetime('2025-7-1')),
(pd.to_datetime('2025-7-1'), pd.to_datetime('2025-10-1'))
]
results = {}
for start_date, end_date in date_ranges:
# 执行向量化筛选和计数
ge_mask = tmp_df.ge(start_date)
le_mask = tmp_df.lt(end_date) # 注意:这里使用lt (<) 而不是 le (<=),以确保区间不重叠且符合常规统计习惯
# 如果包含结束日期,则使用le
final_mask = ge_mask & le_mask
counts = final_mask.sum()
range_label = f"{start_date.strftime('%m/%d/%Y')} - {end_date.strftime('%m/%d/%Y')}"
results[range_label] = counts.tolist() # 将Series转换为列表
# 构建最终的汇总DataFrame
output_df = pd.DataFrame.from_dict(results, orient='index', columns=tmp_df.columns)
print("\n最终漏斗分析汇总表:\n", output_df)输出:
最终漏斗分析汇总表:
stage 1 stage 2 stage 3
01/01/2025 - 04/01/2025 2 1 1
04/01/2025 - 07/01/2025 0 2 1
07/01/2025 - 10/01/2025 0 1 1请注意,在定义日期范围时,通常会使用半开区间[start, end),即包含起始日期但不包含结束日期,以避免区间重叠导致重复计数。因此,在上述代码中,我们将le_mask = tmp_df.le(end_date)改为了le_mask = tmp_df.lt(end_date)。如果业务需求是包含结束日期,则应使用le()。
注意事项与最佳实践
- 数据类型统一: 始终确保DataFrame中的日期列已经正确转换为Pandas的datetime类型。否则,比较操作将无法正确执行。
- 向量化操作: 尽可能利用Pandas的向量化操作(如apply、ge、le、sum等),避免使用Python原生的循环,以获得最佳性能。
- 区间定义: 明确日期范围是闭区间[start, end]、开区间(start, end)还是半开区间[start, end),并相应地选择ge/gt和le/lt进行比较。
- 可读性: 将复杂的逻辑分解为几个清晰的步骤(如生成布尔掩码、组合掩码、求和),可以提高代码的可读性和可维护性。
- 后续分析: 生成的汇总表可以直接用于可视化,例如绘制漏斗图,直观展示不同阶段的转化率。
总结
通过本教程,我们学习了如何利用Pandas的向量化能力,高效且准确地统计DataFrame中多列日期数据在指定时间范围内的行数。关键在于理解并正确应用元素级的布尔比较和列求和操作,避免了常见的筛选误区和低效的循环方法。掌握这种技术,能够有效地支持多阶段流程的性能分析和可视化需求。
以上就是使用Pandas高效统计多列日期数据在指定范围内的行数的详细内容,更多请关注其它相关文章!
# 如何将
# 产品营销和推广哪个靠谱
# 网站建设宣传视频
# 速卖通营销推广策划报告
# 杭州关键词排名专家乐云seo
# 池州seo推广计划
# 广州网站关键词推广
# 郑州品牌创意网站建设
# 惠州seo公司
# 青岛高端网站建设价格
# 托班营销推广方案范文
# 源代码
# python
# 数据包
# 任何一个
# 欧洲
# 这类
# 这种方法
# 行数
# 转换为
# 布尔
# red
# app
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
京东单号查询入口_京东快递订单追踪入口
“音游” × “怪文书” 题材的节奏冒险游戏 《晕晕电波症候群》确定于2026年4月发售!
C#如何安全地从用户上传的XML文件中读取数据? 验证与清理策略
使用CSS更改登录屏幕输入框中PNG图标颜色的策略与局限性
照顾宝贝2小游戏免费秒玩入口
期待已久:小米17 Ultra、小米首款NAS本月登场
外媒分析《GTA6》定价:卖100美元可以但真没必要!
Win11截图该按哪些键 Win11截屏完整流程解析【教程】
C++如何实现单例模式_C++设计模式之线程安全的单例写法
俄罗斯搜索引擎Yandex指南 附2025年免登录官网入口
J*a中实现Go语言select通道多路复用机制
Django AJAX 文件上传教程:解决图片无法保存到模型的常见问题
css子元素高度不一致导致布局错位怎么办_使用align-items:stretch解决高度差异
在J*a中如何使用Stream.map转换元素_Stream映射操作解析
J*aScript Promise链中如何正确终止后续.then执行并处理错误
CSS布局:解决全屏元素100%尺寸与外边距导致的页面溢出问题
msn官网入口地址手机版 msn官方网站手机最新链接
星露谷物语官网入口 星露谷物语游戏官网入口
vivo手机参数配置怎么增强信号_vivo手机参数配置信号增强方法
晋江读书网页版在线登录 晋江读书电脑版官网
PyTorch模型训练准确率不提升:诊断与修复常见指标计算错误
聚水潭ERP登录页面入口 聚水潭ERP官网登录界面
整合Supabase认证与Django模型:跨模式迁移的解决方案
yy漫画网页版官方入口_yy漫画官网登录页面链接
想当下一个《2077》?《心之眼》Steam评价升至"多半好评"
AO3官方镜像站点汇总 AO3同人作品网页版直达链接
c++20的std::jthread是什么_c++可中断线程与RAII式管理
Golang如何实现Web文件静态资源服务器_Golang静态资源服务器开发与实践
微信群消息显示延迟如何解决 微信群消息刷新优化方法
俄罗斯浏览器官网直达链接 俄罗斯浏览器最新在线入口导航
4399网页游戏电脑版全新入口 4399电脑端在线玩指南
谷歌浏览器浏览体验优化_谷歌浏览器新版直连永久可用提示
UC浏览器官网入口2025最新 UC浏览器网页版正式地址
《刺客信条:影》PS5 Pro和Switch 2画面对比
抖音网页版平台入口 抖音网页版官网在线访问教程
qq游戏手机版下载安装_qq游戏移动端入口
MinIO大规模对象列表性能瓶颈深度解析与外部元数据管理策略
品牌机怎么重装系统 联想/戴尔/惠普笔记本恢复出厂系统教程
126邮箱网页版官方入口 126邮箱账号在线登录平台
微博网页版首页入口 微博电脑端官网登录链接
《主播少女的秘密账号迷宫》首支宣传片
C++ string find函数返回值npos详解_C++字符串查找失败的判断条件
AO3最新镜像入口 Archive of Our Own官方平台访问
php源码怎么看淘宝客系统_看php源码淘宝客系统技巧
J*aScript中localStorage数据的获取、清洗与格式化教程
C++如何生成随机数_C++ random库使用方法与范围设置
QQ邮箱官方网站登录入口_QQ邮箱网页版在线使用
如何使用Go和Martini动态服务解码后的图片
响应式容器内容自动缩放与宽高比维持教程
Windows电脑怎么截图最方便_系统自带截图工具的5种神仙用法【技巧】


2025-12-05
浏览次数:次
返回列表