如何用Pandas语法实现DataFrame按时间戳匹配日期范围的关联查询?
Pandas实现时间范围关联(替代SQL的范围JOIN)
你需要的是将带时间戳的DataFrame与包含日期范围的DataFrame做关联,对应SQL中的笛卡尔积加时间过滤逻辑,以下是几种纯Pandas的实现方式:
先明确SQL逻辑对应的需求
你的SQL查询本质是取两个表的笛卡尔积,然后筛选出df1.timestamp落在df2.FromDate和df2.ToDate之间的所有组合,返回双方的所有列。
select df1.*, df2.* from df1, df2 where df1.timestamp >= df2.fromdate and df1.timestamp <= df2.todate
方法一:交叉合并后过滤(小数据集首选)
Pandas 1.2.0及以上支持how='cross'实现笛卡尔积,之后直接用布尔索引过滤时间条件,逻辑最直观。
import pandas as pd # 构造示例数据 df1 = pd.DataFrame({ 'timestamp': pd.to_datetime(['2023-01-05', '2023-02-10', '2023-03-15']), 'value1': [10, 20, 30] }) df2 = pd.DataFrame({ 'FromDate': pd.to_datetime(['2023-01-01', '2023-02-01', '2023-03-01']), 'ToDate': pd.to_datetime(['2023-01-31', '2023-02-28', '2023-03-31']), 'metadata': ['A', 'B', 'C'] }) # 执行交叉合并+过滤 result = df1.merge(df2, how='cross') result = result[(result['timestamp'] >= result['FromDate']) & (result['timestamp'] <= result['ToDate'])]
优缺点:代码简单易读,但如果两个DataFrame数据量大,笛卡尔积会占用大量内存,仅适合小数据集。
方法二:逐行匹配关联(中等数据量适用)
通过apply遍历df1的每一行,找到df2中符合时间范围的记录并合并,避免全量笛卡尔积,内存占用更低。
def match_metadata(row): # 筛选当前timestamp对应的df2行 mask = (df2['FromDate'] <= row['timestamp']) & (df2['ToDate'] >= row['timestamp']) matched_rows = df2[mask].copy() # 把当前df1的列值补充到匹配结果中 matched_rows['timestamp'] = row['timestamp'] matched_rows['value1'] = row['value1'] return matched_rows # 合并所有匹配结果 result = pd.concat([match_metadata(row) for _, row in df1.iterrows()], ignore_index=True) # 调整列顺序,和SQL输出对齐 result = result[['timestamp', 'value1', 'FromDate', 'ToDate', 'metadata']]
优缺点:内存占用比方法一低,但iterrows()遍历速度较慢,适合中等规模数据。
方法三:区间索引匹配(大数据量高效方案)
利用Pandas的IntervalIndex将df2的日期范围转为区间,通过索引快速匹配timestamp对应的区间,效率最高。
# 为df2创建包含日期范围的IntervalIndex(closed='both'表示包含区间两端) df2['date_interval'] = pd.IntervalIndex.from_arrays(df2['FromDate'], df2['ToDate'], closed='both') # 为df1的每个timestamp找到对应的df2行索引 def get_interval_idx(timestamp): try: return df2['date_interval'].get_loc(timestamp) except KeyError: return -1 # 无匹配时返回-1 df1['interval_idx'] = df1['timestamp'].apply(get_interval_idx) # 过滤无匹配的行,然后关联df2 result = df1[df1['interval_idx'] != -1].merge(df2, left_on='interval_idx', right_index=True) # 清理多余列并调整顺序 result = result.drop(['interval_idx', 'date_interval'], axis=1)[['timestamp', 'value1', 'FromDate', 'ToDate', 'metadata']]
优缺点:匹配效率极高,内存占用小,适合大数据量场景;需要处理无匹配的边界情况,逻辑稍复杂。
通用注意事项
- 务必确保所有日期列都是
datetime类型,可通过pd.to_datetime()统一转换,否则时间比较会出错。 - 如果一个timestamp同时匹配多个日期范围,三种方法都会返回所有匹配组合,和SQL逻辑完全一致。
内容的提问来源于stack exchange,提问作者Joanna Boone
相关产品推荐
相关产品推荐

