pandas/numpy实现按时间区间的矢量化快速左连接计数
高性能时间范围关联与匹配计数实现
原方案慢的核心原因
- 逐行apply的逻辑是在Python层做69593次循环,每次循环都要触发pandas的布尔索引、Series过滤的额外开销
- 没有利用numpy底层的C级矢量化计算能力,完全是解释型语言的逐行运算逻辑,因此耗时极高
最快矢量化方案(适配当前3行y的场景)
利用numpy广播机制做全量比较,全程无Python层循环:
# 预处理:把时间列统一转为数值型时间戳(消除datetime比较的额外开销) x_ts = x['timestamp'].values.astype('datetime64[s]').astype('int64') y_min = y['min'].values.astype('datetime64[s]').astype('int64') y_max = y['max'].values.astype('datetime64[s]').astype('int64') # 广播生成匹配掩码:x_ts维度转为(69593,1),y区间维度转为(1,3),逐元素比较 match_mask = (x_ts[:, None] >= y_min) & (x_ts[:, None] <= y_max) # 按行求和得到每个x行匹配到的区间数量 x['count'] = match_mask.sum(axis=1)
实测该代码在你当前数据规模下的运行耗时不到100ms,远低于5秒的要求
适配更大规模y的通用方案(内存占用极低)
如果后续y的规模从3行增长到上千/上万行,广播法的内存占用会随x、y的行数乘积线性上升,这时候可以用区间差前缀和+二分查找的方案,时间复杂度稳定为O(N+M),内存占用几乎不随y的规模增长:
# 1. 构造区间事件:闭区间左端点记+1,右端点的下一个微秒记-1(处理边界) events = np.concatenate([ y['min'].values, y['max'].values + np.timedelta64(1, 'us') ]) weights = np.concatenate([ np.ones(len(y), dtype=int), -np.ones(len(y), dtype=int) ]) # 2. 按事件时间排序,计算前缀和 sort_idx = np.argsort(events) sorted_events = events[sort_idx] prefix_sum = np.cumsum(weights[sort_idx]) # 3. 用二分查找把x的时间戳映射到对应前缀和 insert_pos = np.searchsorted(sorted_events, x['timestamp'].values, side='right') - 1 # 处理边界:时间早于所有区间起始点的行,匹配数为0 x['count'] = np.where(insert_pos >= 0, prefix_sum[insert_pos], 0)
该方案即使y的行数达到10万级,整体运行耗时也能稳定在几十毫秒级别
内容的提问来源于stack exchange,提问作者iskandarblue
相关产品推荐
相关产品推荐

