基于日期范围列表匹配Pandas中日期所属区间位置的优化方法
嘿,你的这个需求我太熟悉了——用循环逐行匹配区间虽然能解决问题,但数据量上去之后效率肯定拉胯。给你两个更高效的向量化优化方案,比原来的循环快N倍:
方案一:用 Pandas 的 cut 函数(Pandas 原生风格)
cut 天生就是用来处理分箱问题的,刚好适配你这种连续且不重叠的区间场景。步骤很简单:
- 先把你的区间列表转换成分箱的边界数组,再生成对应的标签(就是你要的区间位置序号);
- 把 DataFrame 里的日期列转成 datetime 类型(和区间里的 datetime 统一类型);
- 调用
cut完成匹配。
代码示例:
import pandas as pd import datetime # 你的原始数据 df = pd.DataFrame({'fecha': [datetime.date(2018,10,1), datetime.date(2019,1,12), datetime.date(2018,12,25)]}) list_ranges = [(datetime.datetime(2018, 10, 1, 0, 0), datetime.datetime(2018, 10, 15, 0, 0)), (datetime.datetime(2018, 10, 16, 0, 0), datetime.datetime(2018, 10, 31, 0, 0)), (datetime.datetime(2018, 11, 1, 0, 0), datetime.datetime(2018, 11, 15, 0, 0)), (datetime.datetime(2018, 11, 16, 0, 0), datetime.datetime(2018, 11, 30, 0, 0)), (datetime.datetime(2018, 12, 1, 0, 0), datetime.datetime(2018, 12, 15, 0, 0)), (datetime.datetime(2018, 12, 16, 0, 0), datetime.datetime(2018, 12, 31, 0, 0)), (datetime.datetime(2019, 1, 1, 0, 0), datetime.datetime(2019, 1, 15, 0, 0))] # 1. 生成分箱边界和标签 bins = [r[0] for r in list_ranges] + [list_ranges[-1][1]] # 包含所有区间的起始和最后一个区间的结束 labels = range(1, len(list_ranges)+1) # 区间序号1到7 # 2. 把date类型转成datetime类型(和区间统一) df['fecha_dt'] = pd.to_datetime(df['fecha']) # 3. 执行分箱匹配 df['result'] = pd.cut(df['fecha_dt'], bins=bins, labels=labels, include_lowest=True) # 查看结果 print(df[['result']])
方案二:用 NumPy 的 digitize 函数(极致性能首选)
如果你处理的是超大数据集,NumPy 的向量化操作会比 Pandas 更快。digitize 可以快速找到每个元素在有序数组中的位置,刚好匹配你的区间场景(因为你的区间是按时间顺序排列的):
import pandas as pd import datetime import numpy as np # 原始数据同上,省略重复部分 df = pd.DataFrame({'fecha': [datetime.date(2018,10,1), datetime.date(2019,1,12), datetime.date(2018,12,25)]}) list_ranges = [(datetime.datetime(2018, 10, 1, 0, 0), datetime.datetime(2018, 10, 15, 0, 0)), (datetime.datetime(2018, 10, 16, 0, 0), datetime.datetime(2018, 10, 31, 0, 0)), (datetime.datetime(2018, 11, 1, 0, 0), datetime.datetime(2018, 11, 15, 0, 0)), (datetime.datetime(2018, 11, 16, 0, 0), datetime.datetime(2018, 11, 30, 0, 0)), (datetime.datetime(2018, 12, 1, 0, 0), datetime.datetime(2018, 12, 15, 0, 0)), (datetime.datetime(2018, 12, 16, 0, 0), datetime.datetime(2018, 12, 31, 0, 0)), (datetime.datetime(2019, 1, 1, 0, 0), datetime.datetime(2019, 1, 15, 0, 0))] # 1. 转换日期类型并转成NumPy数组 fecha_dt = pd.to_datetime(df['fecha']).to_numpy() # 2. 提取所有区间的起始点(因为区间有序,digitize会自动匹配对应区间) starts = np.array([r[0] for r in list_ranges]) # 3. 用digitize找到每个日期对应的区间序号 df['result'] = np.digitize(fecha_dt, starts, right=False) # 查看结果 print(df[['result']])
为什么这两个方案更好?
原来的 apply 加循环是逐行遍历,时间复杂度是 O(n*m)(n是DataFrame行数,m是区间数),数据量大的时候会非常慢。而上面两个方案都是向量化操作,时间复杂度降到 O(n + m),性能提升非常明显,尤其是当你的DataFrame有几万甚至几十万行的时候。
测试下来,这两个方案都能得到你想要的结果:
| result |
|---|
| 1 |
| 7 |
| 6 |
内容的提问来源于stack exchange,提问作者Á. Garzón
相关产品推荐
相关产品推荐

