如何不使用循环快速在多个DataFrame中匹配日期索引?
高效匹配多DataFrame中的日期索引
需求:给定一组目标日期,逐个匹配多个DataFrame——找到第一个时间范围包含该日期的DataFrame,记录其序号和该日期在对应DataFrame中的最近索引。原循环方法在数据量大时速度极慢,寻求优化方案。
原实现代码:
import pandas as pd from datetime import timedelta df_tomatch_1=pd.DataFrame({'a':[1,2,3,4],'b':[10,20,30,40]}) df_tomatch_1.index=['2023-01-01 10:00','2023-01-01 11:00','2023-01-01 12:00','2023-01-01 13:00'] df_tomatch_2=pd.DataFrame({'c':[1,2,3,4],'d':[10,20,30,40]}) df_tomatch_2.index=['2023-01-02 10:00','2023-01-02 11:00','2023-01-02 12:00','2023-01-02 13:00'] time_tomatch=['2023-01-01 10:30','2023-01-01 12:30','2023-01-02 11:30'] def find_matched_date_index(df_tomatch_list,time_tomatch): indx_time=[] for date_str in time_tomatch: print(date_str) thisdate=pd.to_datetime(date_str) for ix_file,df in enumerate(df_tomatch_list): df.index=pd.to_datetime(df.index) if thisdate>=df.index[0]-timedelta(days=0.01) and thisdate<=df.index[-1]+timedelta(days=0.01): indx_found=df.index.get_indexer([thisdate],method='nearest')[0] indx_time.append([ix_file,indx_found]) break # exit for loop of dataframe return indx_time indx_time=find_matched_date_index([df_tomatch_1,df_tomatch_2],time_tomatch) print(indx_time) # 输出:[[0, 1], [0, 3], [1, 2]]
优化思路与实现
原代码核心低效点:
- 重复转换DataFrame索引为datetime(每次循环都执行
df.index=pd.to_datetime(df.index)) - 逐个遍历目标日期和DataFrame,未利用pandas的向量化运算能力
- 每次调用
get_indexer仅处理单个日期,浪费向量化效率
优化步骤:
- 预处理DataFrame集合:提前转换所有索引为datetime,同时记录每个DataFrame的时间边界(最小/最大索引,加上设置的缓冲
timedelta(days=0.01)),避免重复计算。 - 向量化处理目标日期:一次性将所有目标日期转为datetime数组,批量处理。
- 快速匹配DataFrame:通过向量化的条件判断,为每个目标日期找到第一个符合时间范围的DataFrame。
- 批量计算最近索引:对每个匹配到的DataFrame,批量处理对应目标日期的最近索引计算。
优化后代码:
import pandas as pd from datetime import timedelta # 预处理函数:统一处理DataFrame索引和时间范围 def preprocess_dfs(df_list, buffer_days=0.01): processed = [] for df in df_list: idx = pd.to_datetime(df.index) # 计算带缓冲的时间范围 min_ts = idx.min() - timedelta(days=buffer_days) max_ts = idx.max() + timedelta(days=buffer_days) processed.append({ "idx": idx, "min_ts": min_ts, "max_ts": max_ts }) return processed # 高效匹配函数 def fast_match_date_indices(processed_dfs, target_dates): target_ts = pd.to_datetime(target_dates) results = [] for ts in target_ts: # 找到第一个时间范围包含当前日期的DataFrame for df_idx, df_data in enumerate(processed_dfs): if df_data["min_ts"] <= ts <= df_data["max_ts"]: # 计算最近索引 idx_pos = df_data["idx"].get_indexer([ts], method="nearest")[0] results.append([df_idx, idx_pos]) break return results # 测试用例 df_tomatch_1=pd.DataFrame({'a':[1,2,3,4],'b':[10,20,30,40]}) df_tomatch_1.index=['2023-01-01 10:00','2023-01-01 11:00','2023-01-01 12:00','2023-01-01 13:00'] df_tomatch_2=pd.DataFrame({'c':[1,2,3,4],'d':[10,20,30,40]}) df_tomatch_2.index=['2023-01-02 10:00','2023-01-02 11:00','2023-01-02 12:00','2023-01-02 13:00'] time_tomatch=['2023-01-01 10:30','2023-01-01 12:30','2023-01-02 11:30'] # 预处理 processed_dfs = preprocess_dfs([df_tomatch_1, df_tomatch_2]) # 匹配 indx_time = fast_match_date_indices(processed_dfs, time_tomatch) print(indx_time) # 输出:[[0, 1], [0, 3], [1, 2]]
超大量目标日期的进一步优化
如果目标日期数量达到十万级以上,可完全避免Python层面的循环,改用分组批量处理:
def ultra_fast_match(processed_dfs, target_dates): target_ts = pd.to_datetime(target_dates).to_series(name="target") results = pd.DataFrame(index=target_ts.index, columns=["df_idx", "pos_idx"]) for df_idx, df_data in enumerate(processed_dfs): # 筛选当前DataFrame能覆盖的目标日期,且未被之前的DataFrame匹配 mask = (target_ts >= df_data["min_ts"]) & (target_ts <= df_data["max_ts"]) mask &= results["df_idx"].isna() if mask.any(): # 批量计算这些日期的最近索引 matched_ts = target_ts[mask] pos_indices = df_data["idx"].get_indexer(matched_ts, method="nearest") # 填充结果 results.loc[mask, "df_idx"] = df_idx results.loc[mask, "pos_idx"] = pos_indices # 转为要求的列表格式 return results.astype(int).values.tolist() # 调用示例 indx_time = ultra_fast_match(processed_dfs, time_tomatch) print(indx_time) # 输出:[[0, 1], [0, 3], [1, 2]]
关键优化点说明
- 预处理缓存:只转换一次索引和计算时间范围,避免重复计算
- 向量化筛选:用pandas布尔掩码批量筛选符合条件的日期,替代Python循环
- 批量索引计算:一次调用
get_indexer处理多个日期,利用pandas底层C实现加速
内容的提问来源于stack exchange,提问作者roudan
相关产品推荐
相关产品推荐

